The standard v1.1 error envelope, the HTTP status codes the API returns, and the machine-readable error-code vocabulary.
Every successful v1.1 request returns 200 with the standard success envelope — including requests that match no data, which return 200 with an empty data array and a meta.emptyReason (see The Response Envelope). Every non-200 response returns one standard error envelope, so your error handling can be written once for the whole API.
The error envelope
{
"error": {
"status": 400,
"code": "invalidDateRange",
"message": "The 'from' date must be earlier than or equal to the 'to' date.",
"details": [
{ "field": "from_date", "issue": "mustBeOnOrBeforeTo" }
],
"request": {
"namespace": "NASDAQ",
"identifier": "NVDA",
"from_date": "2026-02-15",
"to_date": "2026-02-01"
},
"help": {
"summary": "Correct the request parameters and retry.",
"examples": [
"/api/v11/stock/NASDAQ/NVDA/short/short_interest/?from_date=2026-02-01&to_date=2026-02-15"
],
"url": null
},
"retryable": false,
"requestId": null
}
}| Field | Meaning |
|---|---|
status | The HTTP status code, repeated in the body for convenience |
code | A stable, machine-readable error code — branch your error handling on this, not on message |
message | A human-readable description; wording may change over time |
details | For validation errors: a list of { "field": …, "issue": … } entries pinpointing each offending parameter. null otherwise |
request | An echo of the parameters the server understood, so you can see what it was reacting to |
help | summary (what to do), examples (corrected example URLs, when applicable), and url (a documentation link, when applicable) |
context | Facts specific to the error, when there are any — for example the plan, allowance and renewal date on a 402, or the rejection count and block end on a 403 apiKeyDenylisted. null on errors that have none. Each code's section below lists its keys |
retryable | true when retrying the same request later may succeed (e.g. rate limiting, transient server errors); false when the request itself must change first |
requestId | A correlation id, populated on 500 responses and on errors that were logged server-side. Quote it when contacting [email protected] — it lets us find the exact log entry for your request. Every response also carries it on the Ortex-Request-Id header, including the ones where this field is null, so you can capture it from a log line without keeping the body |
If you request CSV (?format=csv) and the request fails, the error is returned as a minimal two-column CSV (error_code,message) with the matching HTTP status.
Status codes and error codes
The code vocabulary is closed and stable — new codes are only added for genuinely new failure modes, and existing codes keep their meaning.
400 Bad Request — the request is malformed
400 Bad Request — the request is malformedFix the parameter named in details and resend.
code | Cause |
|---|---|
invalidDateRange | A date is malformed (anything other than YYYY-MM-DD; details[0].issue is invalidFormat), from_date is after to_date, a required date parameter is missing, or the window is longer than the endpoint allows (e.g. 366 days on the enterprise region feeds, one year on ESMA positions); single-date endpoints also return it for a from_date / to_date they do not take |
invalidRegion | The region value is not a recognized region name or ISO-2 code. Also returned when the endpoint covers only some regions and the one you asked for is not one of them (US-only data such as the options PCR snapshot, fails to deliver and the Reg SHO threshold lists; intraday short interest covers the US, Canada, Hong Kong and Australia, so a region containing none of them); details[0].issue is notCovered |
invalidSplitAdjusted | split_adjusted is not a boolean (true/false) |
invalidPointInTime | point_in_time is not a boolean (true/false) (free float) |
invalidMinLagDays | min_lag_days is not a whole number from 0 to 365, or was sent without point_in_time=true (free float). details[0].issue is outOfRange or requiresPointInTime |
invalidPage | page is not a positive integer |
invalidPageSize | page_size is not an integer within the allowed range |
invalidExchangeSymbol | The exchange symbol in the URL path is not recognized |
invalidUniverseName | A universe name is empty, contains spaces, or exceeds 60 characters (universe-maintainer endpoints) |
invalidConstituents | The constituents body is missing, empty, or an item is not a valid identifier: it carries neither ortex_tid nor an exchange + ticker pair, or it carries both at once. An item's optional as_of must be a YYYY-MM-DD date, and is not accepted alongside ortex_tid. details[0].issue distinguishes them (required, notAnObject, missingIdentifier, conflictingIdentifier, invalidAsOf, asOfWithOrtexTid) |
invalidSelection | Exactly one identity selector is required and the request sent both or neither — index/universe on bulk endpoints, filer_cik/filer_name on the activist filer endpoint. Also returned by the Alt Data per-dataset endpoints when market, series and source are each valid but select no series together. Index Rebalance returns it when indices is missing on the detail endpoint, or names an index it does not forecast in the overview's optional filter |
invalidCountry | The country filter is not a covered ISO-2 code (exchanges reference, macro events, peers). The message lists the covered codes |
invalidPeriod | The period selector is not one of the endpoint's accepted values (days-to-cover) |
invalidTypes | The types filter contains an unrecognized value (corporate actions) |
invalidFilingType | The filing_type filter is not a recognized 13D/G schedule type (activist filings) |
invalidFilerKind | The filer_kind filter is not Active or Passive giant (activist filings) |
invalidSeries | The series filter names no series ORTEX publishes, or names one published by several sources and so does not identify a single series. details[0].issue distinguishes them (required, unrecognized, ambiguous); an ambiguous slug is resolved by adding market or source |
invalidMarket | The market filter names a market the Alt Data dataset does not cover. Distinct from invalidRegion: region is the enterprise country allow-list, while market selects the market a series describes — a value space that includes supranational aggregates (EA, EU, EU-EFTA-UK, EUROPE) |
invalidSource | The source filter names a publisher the Alt Data dataset does not draw on |
invalidIdentifier | An identifier in the stock path (tid/…, sid/…, cid/…, isin/…, figi/…) is malformed. details[0].issue says why (mustBeInteger, malformed, checkDigitMismatch). A well-formed identifier that matches nothing is 404 instrumentNotFound instead |
instrumentNotCovered | The listing in the path exists, but the endpoint's data does not cover it — the fails-to-deliver and Reg SHO threshold-list endpoints serve US listings only. details[0].issue is notUsListing. Not the same as 404 instrumentNotFound, which means the identifier matched nothing |
invalidVolumeFromAllExchanges | volume_from_all_exchanges is not true or false (closing prices) |
invalidWeights | The stock-scores weights list is invalid. details[].issue says which rule failed: empty, tooMany, retired, unrecognized, outOfRange, duplicate, noComponentWeight |
invalidNumPeers | num_peers is not an integer from 1 to 1000 (peers). details[0].issue is mustBePositiveInteger or outOfRange |
invalidModel | model is not one of the accepted peer models (peers) |
invalidNumEvents | num_events is not an integer from 1 to 8 (dividend forecast). details[0].issue is mustBePositiveInteger or outOfRange |
invalidHorizon | horizon is not one of first, d90, d30, d7, d1 (dividend forecast history) |
invalidAt | at is not a past ISO 8601 timestamp: malformed, an impossible date, or in the future (intraday short interest) |
invalidCostToBorrow | cost_to_borrow is not true or false (intraday short interest) |
invalidMarketCap | A market_cap, market_cap_min or market_cap_max filter is invalid, or the minimum exceeds the maximum; details[].field names which (intraday short interest) |
invalidAdjustForSplit | adjust_for_split is not true or false (historic volatility) |
invalidAdjustForDividend | adjust_for_dividend is not true or false (historic volatility) |
invalidSince | since is not an ISO 8601 timestamp. Pass back the largest cursor value from your previous pull (updatedAt on snapshot endpoints, publishedAt on Market Intelligence content) |
invalidTransactionCode | transaction_code is not an SEC Form 4 transaction code (US insider transactions), or transaction_type is not one of the global insider feed's values (buy, sell, grant, exercise, …). details[0].field names which was sent |
invalidExpiry | expiry is missing or is not a valid YYYY-MM-DD date (options chain) |
invalidNumStrikes | num_strikes is not an integer from 1 to 500 (options details) |
invalidParameterName | parameter_name is missing or is not one of the option-data fields the endpoint accepts (options details) |
401 Unauthorized — no usable credential was presented
401 Unauthorized — no usable credential was presented401 means we could not identify you: either no key was sent, or the key sent matches no account.
code | Cause |
|---|---|
apiKeyMissing | No API key was supplied |
apiKeyInvalid | The key does not match any account |
402 Payment Required — out of credits
402 Payment Required — out of creditscode | Cause |
|---|---|
creditsExhausted | The key is valid but has no remaining credits for this request |
The response tells you where the account stands, so one 402 is enough to decide what to do: wait for the renewal, top up, or fix a failed payment.
{
"error": {
"status": 402,
"code": "creditsExhausted",
"message": "Your Trader plan's 10,000 credits for the cycle ending 2026-11-10 are used up; they renew on 2026-11-10.",
"details": null,
"request": { "namespace": "NASDAQ", "identifier": "NVDA" },
"context": {
"plan": "Trader",
"allowance": 10000,
"used": 10000,
"remaining": 0,
"requestCost": 2.5,
"cycleEndsAt": "2026-11-10T08:28:56+00:00",
"renews": true,
"subscriptionStatus": "active",
"plansUrl": "https://app.ortex.com/apis/plans"
},
"help": {
"summary": "Credits renew with your subscription cycle; to continue before then, top up or move to a larger plan.",
"examples": [],
"url": "https://app.ortex.com/apis/plans"
},
"retryable": false,
"requestId": null
}
}context key | Meaning |
|---|---|
plan | The plan the key's account is on |
allowance, used, remaining | Credits available to the key right now, summed across the plan cycle and any top-ups: the allowance, what has been spent, and what is left. remaining is 0: a request is served while your balance is above zero, and one that costs more than is left takes the balance to zero, so the next is refused |
requestCost | What this request would have charged, so you can see how far short the balance is |
cycleEndsAt | When the current credit cycle ends |
renews | true when the credits renew at cycleEndsAt; false when they will not, and message says why: a payment that has not succeeded (past_due, unpaid) or a subscription that is cancelled or paused; null when the subscription state is not known |
subscriptionStatus | The subscription state behind renews, for example active, trialing, past_due, canceled |
plansUrl | Where to add credits or change plan |
Stop retrying on a 402. Nothing about the request changes the answer until the cycle renews or credits are added — and a client that keeps sending requests after a 402 is what the automatic block described under 403 apiKeyDenylisted below exists to catch. Check renews: if it is false, the credits will not come back on their own, so retrying at cycleEndsAt will fail too.
403 Forbidden — the key is known but not allowed
403 Forbidden — the key is known but not allowed403 means we identified the key, but the request is refused. Either the key itself is no longer usable (expired, revoked, or its account disabled), or it is usable but not entitled to what was requested.
code | Cause |
|---|---|
apiKeyExpired | The key has passed its expiry date |
apiKeyRevoked | The key has been revoked |
apiKeyDisabled | The account linked to the key is disabled |
insufficientSubscription | Your subscription tier does not include this endpoint category. The universe-maintainer endpoints also return this for the TEST key, which has no account to own a universe |
apiKeyDenylisted | The key is temporarily blocked, in almost every case automatically after it kept sending requests that were being rejected (for example, after a 402). message says until when and after how many rejections. context carries rejections (the count that earned the block), until, liftsOnCreditTopUp (true for an automatic block, which a credit top-up or renewal lifts at once) and block (which automatic block this is within the escalation window, for example auto-block 3 in 14 days; null on a manual block). retryable is true; the Retry-After header says when. Fix the cause before retrying — a block that is lifted and re-earned lasts longer each time |
endpointNotInPlan | This specific endpoint is not part of your plan |
addOnRequired | Your API subscription covers this endpoint, but the endpoint also needs a paid add-on that the ORTEX account owning your key does not have. context.requiredAddOn names it: options on the US options endpoints, which need the Options add-on as well as an API plan. Upgrading your API plan does not help; add the add-on to your account |
seriesNotAvailable | The Alt Data series you named is derived from a commercial source, so its raw values are not available for redistribution. Its ORTEX-computed signals are — use the Alt Data signals endpoint. The catalog's rawAvailable flag tells you which series this applies to |
restrictedRegion | Your key is limited to certain regions and the request falls outside them. Feeds whose rows each belong to a country drop the rows outside your regions instead; endpoints whose data cannot be placed in a region reject a region-limited key with this code |
restrictedDateRange | Your key's access is limited to a date range and the request reaches outside it. On snapshot and reference-list endpoints, which have no date window, a key whose range ends before today gets this code; on dated feeds (Market Intelligence content, Stock Reports) rows older than the range's start are withheld instead and meta.restriction says so. An out-of-region ?ticker= or ?id= on those feeds is 403 restrictedRegion |
404 Not Found — the path or an identifier did not resolve
404 Not Found — the path or an identifier did not resolvecode | Cause |
|---|---|
instrumentNotFound | The stock path's {namespace}/{identifier} did not resolve to a known listing: a venue (exchange symbol, MIC or ISO-2 country code) and a ticker, or a tid / sid / cid / isin / figi identifier. |
indexNotFound | The bulk index value, or an index named on the Index Rebalance detail endpoint, is not an index ORTEX covers |
universeNotFound | The universe name matches no index universe and none of your own universes |
filerNotFound | The filer_cik / filer_name identifier matches no activist filer |
samplePathNotReplaced | The URL still contains the literal placeholders exchange or ticker from a documentation example — replace them with real values |
pageOutOfRange | The result set is non-empty, but the requested page is past the last page |
malformedUrl | The URL path matches no v1.1 endpoint at all — a typo in the path, or a route that does not exist |
insiderNotFound | No insider matches the insider_cik or insider_name you gave (insider history) |
Note that a request that resolves correctly but matches no data is not a 404 — it is a 200 with meta.emptyReason (see The Response Envelope).
405 Method Not Allowed — wrong HTTP method for this route
405 Method Not Allowed — wrong HTTP method for this routecode | Cause |
|---|---|
methodNotAllowed | The route exists but does not support the HTTP method used (e.g. PUT where only GET or PATCH is supported). The Allow response header lists the supported methods |
409 Conflict — the request conflicts with the current state
409 Conflict — the request conflicts with the current stateThe request was well-formed and authorized, but conflicts with what already exists (universe-maintainer endpoints, and bulk endpoints selecting one of your universes).
code | Cause |
|---|---|
universeNameConflict | You already own a universe with the requested name (on create or rename) |
universeSizeExceeded | The universe holds, or the request would leave it holding, more constituents than your plan allows per universe. When adding, nothing is added. On a bulk endpoint, universe= is refused while the universe is over the limit; nothing is removed, so trim the universe or upgrade to query it again |
429 Too Many Requests — slow down
429 Too Many Requests — slow downcode | Cause |
|---|---|
rateLimitExceeded | You have exceeded the request rate limit. retryable is true — back off and retry |
Use the Retry-After header rather than the retryable field to time your retry. A 429 carries Retry-After with the number of seconds to wait, plus RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset describing your allowance. Retry-After is a standard HTTP header, so most retry helpers act on it with no code from you:
# urllib3 / requests — honours Retry-After automatically
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
retry = Retry(total=5, backoff_factor=0.5, status_forcelist=[429, 503], respect_retry_after_header=True)
session.mount("https://", HTTPAdapter(max_retries=retry))retryable in the body says whether a retry can succeed; Retry-After says when. Read both if you are handling retries yourself, and prefer the header if you are using a library.
500 Internal Server Error — our fault
500 Internal Server Error — our faultcode | Cause |
|---|---|
internalServerError | An unexpected server-side failure. The response never includes internal detail; it does include a requestId, which is logged on our side. If the error persists, email [email protected] and include the requestId |
Any other unexpected condition (for example, requesting CSV from a JSON-only endpoint, which yields 406 Not Acceptable) is wrapped in the same envelope with the generic code requestError.
