NuminorBeta

API Access · Data Products

Construct Data API

Subscribe to a Construct Data product, generate an API key, and pull daily-refresh factor data as signed-URL parquet. REST, Bearer-authed, no SDK required.

Getting started

  1. 1

    Subscribe to a product at /lab/data — each subscription entitles your key to that product's data.

  2. 2

    Generate an API key on your account page. The full key is shown once — store it securely.

  3. 3

    Make your first request — the manifest endpoint is the cheapest check that your key + subscription work:

curl -H "Authorization: Bearer $NUMINOR_KEY" \
  https://api.numinor.io/v1/constructs/sam-amplifier/manifest

Authentication

Send your key as a Bearer token on every request. One key works across all of your subscribed products (entitlements resolve from your active subscriptions). Matrix-tier keys do not grant raw REST export — that's an API-tier feature.

Authorization: Bearer nm_live_…

Core concepts

Point-in-time delay
The API tier serves current data; the in-Matrix tier is delayed 30 days. Every row is explicit about its point-in-time so backtests are leak-free.
Signed URLs
Data endpoints return a short-lived signed S3 URL (≈4h). Download the parquet directly from S3 — that transfer is not metered or rate-limited.
Parquet format
All files are Apache Parquet. Read them with pyarrow, DuckDB, or polars — no server-side query endpoint needed for the common case.
Rate limit
Up to 1000 requests per minute per key. Over the limit returns 429 with a Retry-After header.

Endpoint reference

Base URL: https://api.numinor.io/v1

GET/constructs/{sku}/manifest

Data freshness, coverage window, and signed-URL TTL for a product.

ParameterInDescription
sku*pathProduct slug, e.g. sam-amplifier.
curl -H "Authorization: Bearer $NUMINOR_KEY" \
  https://api.numinor.io/v1/constructs/sam-amplifier/manifest
Response example
{
  "sku": "sam-amplifier-construct-v1",
  "tier": "api",
  "status": "green",
  "latest_trade_date": "2026-06-18",
  "signed_url_ttl_seconds": 14400,
  "historical_coverage": {
    "start": "2016-01-04",
    "end": "2026-06-18"
  }
}
GET/constructs/{sku}/day/{date}

A signed URL to one trading day's partition.

ParameterInDescription
sku*pathProduct slug, e.g. sam-amplifier.
date*pathTrading day as YYYYMMDD or YYYY-MM-DD.
# get a signed URL for one trading day, then download the parquet
curl -H "Authorization: Bearer $NUMINOR_KEY" \
  https://api.numinor.io/v1/constructs/sam-amplifier/day/20260515
Response example
{
  "url": "https://numinor-construct-data.s3.ap-northeast-2.amazonaws.com/…&X-Amz-Signature=…",
  "trade_date": "2026-05-15",
  "expires_in": 14400,
  "format": "parquet"
}
GET/constructs/{sku}/range

Signed URLs for every published trading day in an inclusive date range.

ParameterInDescription
sku*pathProduct slug, e.g. sam-amplifier.
start*queryRange start (inclusive), YYYYMMDD.
end*queryRange end (inclusive), YYYYMMDD.
curl -H "Authorization: Bearer $NUMINOR_KEY" \
  "https://api.numinor.io/v1/constructs/sam-amplifier/range?start=20260501&end=20260531"
Response example
{
  "sku": "sam-amplifier-construct-v1",
  "tier": "api",
  "count": 21,
  "days": [
    {
      "trade_date": "2026-05-06",
      "url": "https://…signed…"
    }
  ],
  "format": "parquet"
}
GET/constructs/{sku}/historical

A signed URL to the full historical bulk file (2016 → latest).

ParameterInDescription
sku*pathProduct slug, e.g. sam-amplifier.
curl -H "Authorization: Bearer $NUMINOR_KEY" \
  https://api.numinor.io/v1/constructs/sam-amplifier/historical
Response example
{
  "url": "https://…signed-bulk-file…",
  "expires_in": 14400,
  "format": "parquet"
}
POST/constructs/{sku}/query

Inline filtered query — on the roadmap (returns 501 today).

ParameterInDescription
sku*pathProduct slug, e.g. sam-amplifier.
# roadmap — returns 501 today
curl -X POST -H "Authorization: Bearer $NUMINOR_KEY" \
  https://api.numinor.io/v1/constructs/sam-amplifier/query
Response example
{
  "ok": false,
  "error": "not_implemented",
  "detail": "Roadmap — fetch signed URLs via /day or /range and read the parquet locally."
}

Field-level export

Buy individual source fields — assembled as a cross-dataset recipe in the Matrix — and export them here. There's no product slug: one call exports every field your key's owner subscribes to. Export is async — POST returns a job id, poll it until the manifest is ready, then download one projected parquet per table from its signed URL. Each export re-materializes current data, so the same call always delivers fresh rows; URLs expire in ~3 days and your recipe is held indefinitely.

Base URL: https://api.numinor.io/v1

GET/field-export/status

Cheap freshness check for a scheduled/conditional pull: returns your recipe's current vintage (no data, no materialization). Compare it — or use the ETag / If-None-Match header (304 when unchanged) — and only run /field-export when it changed.

# scheduled / conditional pull — only export when the data actually changed
LAST=$(cat .numinor_vintage 2>/dev/null)
NOW=$(curl -s -H "Authorization: Bearer $NUMINOR_KEY" https://api.numinor.io/v1/field-export/status | jq -r .vintage)
[ "$NOW" = "$LAST" ] && { echo "no new data"; exit 0; }
echo "$NOW" > .numinor_vintage
# → changed: run the export below, then load the fresh files
Response example
{
  "ok": true,
  "vintage": "a1b2c3d4e5f6",
  "datasets": [
    {
      "dataset": "rfp_bids",
      "vintage": "9a2f1c4e7b83",
      "latest_known_date": "20260728",
      "major": 3
    }
  ],
  "note": "Compare `vintage` (or use the ETag) to your last pull; export only when it changed."
}
POST/field-export

Kick an async export of your entire active field recipe. Returns a job id immediately (202).

# 1) kick the export → job id
JOB=$(curl -s -X POST -H "Authorization: Bearer $NUMINOR_KEY" \
  https://api.numinor.io/v1/field-export | jq -r .job_id)

# 2) poll until ready
until [ "$(curl -s -H "Authorization: Bearer $NUMINOR_KEY" \
  https://api.numinor.io/v1/field-export/jobs/$JOB | jq -r .status)" = "ready" ]; do sleep 3; done

# 3) grab the signed per-table URLs
curl -s -H "Authorization: Bearer $NUMINOR_KEY" \
  https://api.numinor.io/v1/field-export/jobs/$JOB | jq -r '.manifest.tables[].file.url'
Response example
{
  "ok": true,
  "job_id": "b3f1c0a2-5e6d-4c7b-9a01-2f3e4d5c6b7a",
  "status": "pending",
  "reused": false,
  "poll_url": "/v1/field-export/jobs/b3f1c0a2-5e6d-4c7b-9a01-2f3e4d5c6b7a"
}
GET/field-export/jobs/{job_id}

Poll a job. When status is ready it carries the signed, per-table download manifest; failed carries a reason.

ParameterInDescription
job_id*pathThe job id returned by POST /field-export.
curl -H "Authorization: Bearer $NUMINOR_KEY" \
  https://api.numinor.io/v1/field-export/jobs/b3f1c0a2-5e6d-4c7b-9a01-2f3e4d5c6b7a
Response example
{
  "ok": true,
  "status": "ready",
  "manifest": {
    "recipe": {
      "datasets": [
        "sam"
      ],
      "table_count": 1,
      "paid_field_count": 2
    },
    "url_expires_in_seconds": 259200,
    "tables": [
      {
        "dataset": "sam",
        "table": "fin_secu_sam_product",
        "rows": 991882,
        "columns": [
          "secu",
          "report_date",
          "product_income",
          "_known_date",
          "_panel_op"
        ],
        "keys": [
          "secu",
          "report_date"
        ],
        "file": {
          "name": "sam__fin_secu_sam_product.parquet",
          "url": "https://…signed…"
        }
      }
    ],
    "readme": "Each file is one table, projected to your fields + free join keys + version columns…"
  }
}

Errors

StatusErrorMeaning
401missing_bearer_token · invalid_api_keyMissing or invalid API key.
403not_subscribed · rest_requires_api_tierYour key has no API-tier subscription for this product (or only Matrix tier).
404unknown_construct · no_partitionUnknown product, or no published partition for that date.
400bad_date · bad_range · missing_paramsMalformed date or range, or missing query parameters.
413range_too_largeRange spans too many days — use the historical bulk file instead.
429rate_limitedRate limit exceeded (1000/min). Honor the Retry-After header.
501not_implementedEndpoint is on the roadmap and not implemented yet.

Products

Ready to build? View pricing, then generate a key.