The Response Envelope

How every ORTEX v1.1 API response is structured — the standard envelope, and how stock timeseries endpoints differ from bulk daily endpoints.

Every endpoint under /api/v11/ returns the same JSON envelope. Once you have parsed one v1.1 response, you have parsed them all: the data rows always live in data, the row counts and page links live in pagination, the credits and response metadata live in meta, and the envelope always tells you exactly what the server understood your request to be — including which of your parameters it did not use.

Every non-200 response uses a single standard error envelope instead — see Response & Error Codes.

The success envelope

A 200 response from a stock timeseries endpoint looks like this:

{
  "request": {
    "namespace": "NASDAQ",
    "identifier": "NVDA",
    "from_date": "2026-02-02",
    "to_date": "2026-02-06"
  },
  "instrument": {
    "exchange_symbol": "NASDAQ",
    "ticker": "NVDA",
    "companyName": "NVIDIA Corporation",
    "ortexCid": 1071589,
    "ortexSid": 3006310,
    "ortexTid": 6684014
  },
  "resolved": {
    "from_date": "2026-02-02",
    "to_date": "2026-02-06",
    "page": 1,
    "page_size": 100,
    "defaultedFields": ["page", "page_size"]
  },
  "ignored": [
    { "parameter": "region",
      "reason": "notSupportedByEndpoint",
      "note": "Region selection is an enterprise feature. On this endpoint, choose the instruments with 'index' or 'universe'." },
    { "parameter": "form_date",
      "reason": "unknownParameter" }
  ],
  "pagination": {
    "next": null,
    "previous": null,
    "rowsThisPage": 5,
    "totalRows": 5
  },
  "data": [
    {
      "date": "2026-02-02",
      "siShares": 245123456
    }
  ],
  "meta": {
    "credits": {
      "used": 1.25,
      "remaining": 99934656.53
    },
    "responseGeneratedAt": "2026-02-06T14:21:09Z"
  }
}

(The data rows above are illustrative — each endpoint documents its own row fields. Row field names are always camelCase.)

request — what you sent

A literal echo of the parameters you supplied, using the same names as the query parameters (from_date, to_date, page, page_size). The two stock path segments are always present, as namespace and identifier, because they are mandatory parts of the URL; everything else appears only if you explicitly sent it. Server-applied defaults never appear here — they appear in resolved.

resolved — what the server used

The value the server actually used for every parameter the endpoint accepts, whether you supplied it or not. defaultedFields lists the parameters whose values came from server defaults rather than your input.

Together, request and resolved answer two different questions: "what did I send?" and "what did the server do with it?". If a response surprises you, compare the two — for example, a request with no dates will show the server-chosen date window in resolved with from_date / to_date listed in defaultedFields.

ignored — what the server did not use

Query parameters you sent that this endpoint does not accept. Present only when there is at least one, so a well-formed request's response is unchanged.

An endpoint accepts exactly the parameters listed in its reference page. Anything else is not applied — it cannot change which data you get back — and is reported here:

FieldPresentMeaning
parameteralwaysThe name as you sent it.
reasonalwaysnotSupportedByEndpoint — a real ORTEX API parameter that this endpoint does not accept. unknownParameter — not an ORTEX parameter at all: a typo, a cache-buster, a stray tracking parameter.
noteon notSupportedByEndpointWhat to use instead. Wording may change; branch on reason, not on this text.

reason is a closed vocabulary, like the error codes — so ignored == [] is a useful assertion in your own test suite. It turns a misnamed parameter into a failure at integration time instead of a puzzling response six months later.

Worth knowing about two cases:

  • A parameter you sent is not applied, even where it looks plausible. An endpoint uses only the parameters on its own reference page — one borrowed from a different endpoint, or from the v1 API, is reported here rather than guessed at.
  • ignored never appears on a 401. A request that fails authentication is answered before any parameter is read, so that response stays the same shape whatever you send.

instrument — the resolved identity (stock endpoints)

Stock endpoints confirm exactly which listing your request resolved to. You name the stock with the two path segments {namespace}/{identifier}: an exchange and a ticker, or one identifier — /stock/tid/{ortexTid}/…, /stock/sid/{ortexSid}/…, /stock/cid/{ortexCid}/…, /stock/isin/{ISIN}/… or /stock/figi/{FIGI}/… (an exchange-level or share-class FIGI). A security or company id, or an ISIN, resolves to the primary listing as of your anchor date. request echoes what you sent (e.g. {"namespace": "isin", "identifier": "US00165C1045"}); instrument reports the listing. The rules are on Identifying an instrument.

FieldMeaning
exchange_symbolThe exchange symbol of the resolved listing; with ticker, it pastes straight back into a stock path
tickerThe ticker of the resolved listing
companyNameThe company's name
ortexCidThe public ORTEX company id
ortexSidThe public ORTEX security id
ortexTidThe public ORTEX trading-item (listing) id
isinThe security's ISIN as of the anchor date
figiThe listing's exchange-level FIGI
micThe listing's venue (segment) MIC
operatingMicThe operating MIC of that venue — the form the path accepts in place of an exchange symbol

Tickers are reused over time, so the ticker in your request is resolved as of a date: the date you supply on single-date endpoints, or the from_date on timeseries endpoints. From that anchor, the response follows the listing, not the ticker string:

  • If the company changed its ticker during your date range, you still receive its data uninterrupted through to_date.
  • If a different company took over the ticker during your range, its data is never mixed in — the response only ever contains the company identified in the instrument block.
  • To query the other company that used the same ticker on that exchange, move from_date into the period when that company held it.

The ORTEX ids are stable public identifiers and use the same vocabulary everywhere in v1.1 — the ortexCid in a stock envelope matches the ortexCid column in a bulk daily row, so you can join across endpoints.

pagination

Where this response sits in the result set — the page links, and the row counts that describe them:

FieldMeaning
nextAbsolute URL of the next page, or null if this is the last page
previousAbsolute URL of the previous page, or null if this is the first page
rowsThisPageNumber of rows in this response — the length of data
totalRowsTotal number of matching rows across all pages

To walk a large result set, follow next until it is null; totalRows tells you up front how many rows you are working through, and rowsThisPage how many arrived in this one.

CSV responses are never paginated, so next and previous are both null there while the counts are still reported.

data

The rows. Field names are camelCase (siShares, daysToCover, …) and are documented per endpoint. What identity information each row carries depends on the endpoint shape — see the next section.

meta

FieldPresentMeaning
emptyReasonalways; null when data has rowsWhy the result is empty — see below
availabilityonly on an empty result, when knownThe date range for which data does exist for this instrument (from_date / to_date)
creditscredit-metered endpoints onlyused (this page) and remaining (your balance). Omitted entirely on endpoints where credits do not apply (e.g. enterprise keys). See Credits and Pricing
restrictionenterprise reference lists and feeds, only when your key's history limit removed older rowsThe date range the response was limited to by your key's restriction (for example, Market Intelligence content and Stock Reports older than your key's history limit are withheld)
testKeyNoteTEST-key requests onlyReminder that dates, paging, and result counts are system-controlled for the TEST key
responseGeneratedAtalwaysISO-8601 UTC timestamp of when the response was generated

Stock timeseries vs bulk daily endpoints

Most v1.1 endpoints come in one of two shapes, and the envelope's identity block differs between them. (A third — market-level series, which have a date axis and no company at all — is described below.)

Stock timeseriesBulk daily
One request returnsone instrument, many datesmany instruments, one date
Identity block in the envelopeinstrument (see above)scope — what was queried, e.g. {"index": "US500", "date": "2026-04-09"} on a bulk endpoint, or {"region": "US", "date": "2026-04-09"} on an enterprise endpoint
Identity on each data rownone — rows carry date + measures onlyfull per-row identifier block (see below)
Date parametersfrom_date / to_date windowsingle date

The rule of thumb: identity is stated exactly once. A stock timeseries response is entirely about one listing, so the identity lives once in the envelope's instrument block and the rows stay lean. A bulk daily response covers many listings, so there is no single instrument to put in the envelope — instead every row identifies itself:

{
  "date": "2026-04-09",
  "company": "NVIDIA Corporation",
  "ticker": "NVDA",
  "exchange": "NASDAQ",
  "operatingMic": "XNAS",
  "mic": "XNAS",
  "isin": "US67066G1040",
  "figi": "BBG000BBJQV0",
  "shareClassFigi": "BBG001S5TZJ6",
  "ortexCid": 1071589,
  "ortexSid": 3006310,
  "ortexTid": 6684014,
  "mainTid": true,
  "tidCurrency": "USD",
  "exchangeCountry": "United States",
  "...": "endpoint-specific measures"
}

If no date is supplied to a bulk daily endpoint, the server defaults to the most recent date for which data exists — the resolved date is always reported back in the envelope (scope.date), so you always know exactly which day you received. An intraday snapshot adds scope.at (ISO 8601, UTC) — the instant the values are as of — and scope.date is then the UTC date of at. Region feeds that take a date window instead of a single date (the enterprise event feeds, for example) report the window in resolved and put the region plus any filter they applied in scope.

Where a stock timeseries endpoint and a bulk daily endpoint expose the same dataset, a timeseries row equals the bulk row for the same listing and date — the two shapes are different cuts of the same data, joinable on the ORTEX ids plus date.

Market-level series: a date window and no company

A few datasets describe a market rather than a listing — national unemployment and
inflation prints, industrial production, Medicare enrolment, US trucking capacity. They are
served from /api/v11/market/… (/api/v11/enterprise/market/… on an enterprise key), and their envelope is
the same as every other one with a single difference: there is no identity block at all.

That is deliberate rather than an omission. There is no instrument to name, no universe and no
region scope — the rows describe whichever markets your filters selected, and attaching one
of them to the envelope would describe a multi-market response as being about a single market.
Each row carries its own market and seriesSlug instead, exactly as a bulk row carries its
own identifiers. The macro-events calendar has the same shape; its rows carry countryIso2 in place of
market.

Everything else is unchanged: resolved carries the from_date / to_date window the server
used, meta.availability appears on an empty result, and pagination works as it does
everywhere.

{
  "request": { "market": "DE", "from_date": "2026-01-01", "to_date": "2026-03-31" },
  "resolved": { "from_date": "2026-01-01", "to_date": "2026-03-31", "page": 1, "page_size": 100, "defaultedFields": ["page", "page_size"] },
  "pagination": { "next": null, "previous": null, "rowsThisPage": 3, "totalRows": 3 },
  "data": [
    {
      "market": "DE",
      "seriesSlug": "hicp_annual_rate_de",
      "seriesName": "HICP annual rate, Germany",
      "sourceSlug": "eurostat_macro",
      "periodDate": "2026-03-01",
      "publishedAt": "2026-04-01T09:00:00Z",
      "value": "2.300000",
      "unit": "%",
      "valueType": "percent",
      "frequency": "monthly",
      "entityRef": "DE"
    }
  ],
  "meta": { "credits": { "used": 0.25, "remaining": 4821.5 }, "responseGeneratedAt": "2026-04-09T12:00:00Z" }
}

from_date is required on these endpoints, as on every other v1.1 timeseries: without it
the request means "the oldest page of everything we hold", which is never what was intended.

Empty results are still 200

A request that matches no data is not an error: you get a 200 with data: [] and meta.emptyReason explaining why:

emptyReasonMeaning
requestedRangeOutsideAvailableDataData exists for this instrument, but none inside your date range — check meta.availability for the range that does exist
noDataInRequestedRangeYour range overlaps the available range, but no rows fall on the requested dates
noDataAvailableForInstrumentWe hold no data of this type for this instrument at all
notPublishedORTEX holds the data but has not published it to customers yet: the dividend forecast endpoints, where forecasts are not yet published for the listing's country

404 is reserved for identifiers that don't resolve (unknown instrument, unknown universe) — see Response & Error Codes.

Single-stock endpoints require from_date. A request without it returns 400 invalidDateRange; to_date remains optional and defaults to today. date does not substitute for it — it is not accepted on these endpoints and appears in ignored.

Response headers

The body is the source of truth. Headers carry only what a caller who cannot read the body needs — a CSV download, or a failed request you are reading from a log or a proxy without the payload.

They are sent on every response, JSON and CSV alike, so you can rely on them either way.

HeaderSent onEquivalent in the body
Ortex-Request-Idevery responseerror.requestId. Quote this if you contact support — it identifies the request without needing the payload
Ortex-Total-Rowslist responsespagination.totalRows
Ortex-Rows-This-Pagelist responsespagination.rowsThisPage
Ortex-From-Date, Ortex-To-Dateendpoints taking a date rangeresolved.from_date / resolved.to_date — the window actually served, so it reflects any trial-key trim. A bound you left open on an optional window is not sent
Ortex-Data-Dateendpoints taking a single dateThe resolved date: the one you asked for or, if you omitted date, the date served (the same value as scope.date). Not sent on an empty result when you gave no date
Ortex-Credits-Used, Ortex-Credits-Remainingcredit-metered endpointsmeta.credits
Ortex-Empty-Reasonempty resultsmeta.emptyReason — on a CSV download this is what distinguishes "no data" from "your filter matched nothing"
Ortex-Ignored-Parameterswhen you sent a parameter the endpoint does not acceptThe ignored block's parameter names, comma-separated. The header is the flag; the body carries the reason and the suggested alternative for each
Ortex-Restrictiontrial keys whose window was trimmedmeta.restriction.message
Retry-After429Seconds to wait before retrying. Standard HTTP header — urllib3, axios-retry and curl --retry honour it automatically
RateLimit-Limit, RateLimit-Remaining, RateLimit-Resetrate-limited endpointsYour per-second allowance, what is left of it, and when it resets

What is deliberately not in a header

The envelope is not mirrored wholesale, and this is a decision rather than an omission. There is no header for instrument, for the pagination links, or for data.

Headers cannot represent nested values without inventing an encoding, and they are size-capped by CDNs and load balancers in a way a response body is not. If you need anything beyond the table above, read it from the body.

Deprecated X- header names

Endpoints with no date parameter (reference lists, catalogs, and present-tense snapshots such as peers, fundamentals and listings) send none of the three date headers.

The count, date, credit and restriction headers are also sent under their previous X- names — X-Total-Rows, X-Rows-This-Page, X-From-Date, X-To-Date, X-Data-Date, X-Credits-Used, X-Credits-Remaining, X-Restriction.

Those names are deprecated. They still work, and no removal date is set yet — we will announce one well in advance. New integrations should read the Ortex- names, which match the Ortex-Api-Key header you already send.

Migrating is a prefix swap: the part after the prefix is unchanged, so X-Total-Rows becomes Ortex-Total-Rows. The three newest headers — Ortex-Request-Id, Ortex-Empty-Reason and Ortex-Ignored-Parameters — have no X- equivalent.

CSV downloads

Endpoints with tabular data also serve CSV: send ?format=csv or an Accept: text/csv header. Because CSV cannot represent a nested envelope:

A few endpoints return rows that are nested objects rather than columns — the fundamentals endpoints, Market Intelligence content, Stock Reports and option details — and are JSON only. Their reference pages say so, and format=csv is not offered on them.

  • The body contains only the data rows (one header line, then rows).
  • The result is not paginated — you get every matching row in one download.
  • The envelope metadata is available on response headers — which are sent on every response, JSON included. CSV is simply the case where they are your only way to read it.
  • A Content-Disposition: attachment header is set so browsers download the file.

Endpoints whose data is not tabular are JSON-only and respond 406 Not Acceptable to a CSV request; each endpoint's reference page states whether CSV is offered.