if a call to the edgeful API isn't behaving the way you expect, this is the page to check first. the API returns standard HTTP status codes, and most of the errors you'll see come from a small set of common mistakes — wrong ticker format, missing header, wrong date format, hitting your tier's limits.
this article covers every error code, what it means, and the fastest way to fix it.
the error codes at a glance
status | meaning | most common cause |
200 | success | none — request worked |
401 | unauthorized | missing, malformed, or revoked API key |
403 | forbidden | your plan doesn't include that report, ticker, date range, or live data (the |
404 | not found | either a path that doesn't exist (e.g. |
422 | validation error | a parameter is missing or in the wrong format |
429 | too many requests | you hit the rate limit |
500 / 502 / 503 | server error | something's wrong on our end — retry with backoff |
the 2 you'll see most often are 422 (your request was malformed) and 429 (you're sending too many requests). 401 usually means you forgot the header or you're using a revoked key.
there's no /probabilities endpoint (and other 404s)
a 404 has two causes, and the detail in the response body tells you which. "detail": "Not Found" means the route you called doesn't exist. "detail": "No historical data available" means the route is fine and the slug is fine — we just don't carry that ticker, so check it against the market_type table further down before you go hunting for a bad slug. taking the route case first: the most common version of it is guessing a metric-named path like /probabilities, /winrate, or /stats. none of those exist — the edgeful API isn't organized by metric name.
every call is a report-slug call: you request a specific report by its slug — like gap-fill-standard or opening-range-breakout-standard — against the base URL https://api.edgeful.com. API v1 is served at the root of that host — there's no /v1 segment in the path.
so if you're getting a 404, check the report slug against the catalog rather than guessing an endpoint name. the full, current list of valid report slugs lives in the docs at edgeful.com/docs (report catalog under api-reference/reports). slugs are case-sensitive and use dashes.
200 OK, but not the breakdown you expected
not every wrong call returns an error code. if a request succeeds but the response doesn't carry the split you were looking for, the slug is probably fine — you're just on the wrong variant of the right report.
most reports have several endpoints, one per view on the report page, all computed from the same days. -standard returns the headline aggregate; the by-... variants return the finer breakdowns — directional splits, conditional cuts, per-weekday views. asking -standard for a breakdown that lives on a variant returns a valid 200 that simply answers a different question, so there's no error to tip you off.
if the response shape looks right but the categories don't match what you're after, check the report's variant list before concluding the data isn't collected. the report library names the variants for every report, and the docs catalog under api-reference/reports has the full slug list with each response shape.
401 unauthorized
your key is missing, malformed, or revoked. the response body looks like this:
{"app_exception": "Unauthorized", "context": null}what to check, in order:
is the
Authorizationheader included on the request? every call needs it — no session, no cookie.is the header exactly
Authorization: Bearer <your-key>? single space between Bearer and the key, no colon after Bearer, no quotes around the key value.is the key value complete? plaintext keys are only shown once at creation — if you copied a masked version from the dashboard, that won't authenticate.
did you regenerate this key recently? if you regenerated it in the API dashboard, the old value stopped working immediately. update your code with the new value.
was the key revoked from the dashboard? a deleted key will return 401 forever — generate a new one. if the header is correct and the key is current, double-check that you're not accidentally hitting a different environment (e.g., a staging URL with a production key).
are you testing in the docs "try it" playground? the playground accepts a bare key (no
Bearerprefix) — it auto-addsBeareron the wire. if you pasteBearer ef_live_...into the playground key field, you'll get a 401 because it sendsBearer Bearer ef_live_.... paste just the key.
403 forbidden — "I can no longer pull reports"
a 403 usually means your key is valid, but your plan doesn't include what you asked for. the response body carries a short code that tells you which limit you hit:
missing_entitlement: there's no active API plan on the account, usually a lapsed or cancelled subscriptionreport_not_allowed: that report isn't in your plan (essential covers 3 reports)ticker_not_allowed: that ticker isn't in your plan (essential covers 4 test tickers)history_range_exceeded: your dates go further back than your plan's lookbacklive_data_not_allowed: the live what's in play and screener endpoints aren't on your plan
if the code points at your plan, change the request or check what your plan includes in rate limits and tier differences. if a request that used to work suddenly returns 403 without one of those codes, the cause is usually one of these:
your key was rotated or revoked. if you regenerated or deleted the key in the API dashboard, the old value stops working immediately. some HTTP clients surface that as a 403 rather than a 401. generate a fresh key and update your code with the new value.
you're hitting the wrong host. the API lives at
https://api.edgeful.com. requests sent towww.edgeful.comor the docs site can come back 403 from the web layer, not from the API.an edge/proxy or firewall is blocking the call. a corporate network, VPN, or security proxy can return 403 before the request ever reaches us. test the same request from a different network, or in the docs' "try it" panel — if it works there, the block is on your side.
your subscription lapsed. if a payment failed and your plan moved to an unpaid state, API access pauses with it. check manage account → payment details and restore the card if needed.
if the key is current, you're hitting api.edgeful.com, your plan is active, and it still 403s, send support the exact request URL (mask your key), the full response body, and a timestamp — that's a case we want to see.
422 validation error
something about your request doesn't match what the endpoint expects. the response body shows you which field and why:
{
"detail": [
{
"loc": ["query", "start_date"],
"msg": "invalid date format",
"type": "value_error.date"
}
]
}the loc field tells you which parameter is wrong. the most common 422 causes:
wrong ticker format for the market type
ticker format depends on market_type. mixing these up is the #1 source of 422s.
market_type | ticker format | example |
stock | plain symbol |
|
forex | 6-character pair |
|
crypto | contract pair |
|
futures | root symbol |
|
a ticker outside the format in the table does not come back as a 422. it returns 404 with "detail": "No historical data available" — that is what you get for ES-MAR2024, for BTC-USD, and for a contract-coded futures symbol like ESU6. edgeful carries continuous contracts, so use the root symbol. a ticker containing a slash, like EUR/USD, breaks the path itself and returns "detail": "Not Found" instead.
wrong date or time format
dates must be
YYYY-MM-DD(e.g.,2024-01-01). not01/01/2024, notJan 1 2024.times must be
HH:MM:SS(e.g.,09:30:00). not9:30 AM, not09:30.
missing required parameters on intraday endpoints
intraday endpoints (anything under intraday_calculation) require start_time, end_time, and timezone in addition to the date range. if you leave one out, you'll get a 422 telling you which parameter is missing.
session times for intraday endpoints
intraday endpoints (anything under intraday_calculation) require start_time, end_time, and timezone. these aren't free-form — they're canonical presets per market_type and session. using anything else returns 422.
the canonical presets live on the session presets docs page. full reference:
market_type | session | start_time | end_time | timezone |
forex | New York |
|
|
|
forex | London |
|
|
|
forex | Asia |
|
|
|
futures | New York |
|
|
|
futures | London |
|
|
|
futures | Asia |
|
|
|
crypto | New York |
|
|
|
crypto | London |
|
|
|
crypto | Asia |
|
|
|
stock | New York |
|
|
|
use these values exactly — the API will not normalize 09:30 to 09:30:00, for example. stock only has the New York session; the other three market types have NY / London / Asia.
daily-session presets. a few daily reports support an optional custom-session aggregation (provide start_time + end_time on a daily endpoint and it'll roll bars into your custom session). these use a separate set of preset values:
market_type | session | start_time | end_time | timezone |
forex | Daily |
|
|
|
futures | Daily |
|
|
|
crypto | Daily |
|
|
|
crypto | Crypto Daily |
|
|
|
these are not an automatic fallback for the intraday endpoints — only the daily reports that explicitly accept custom-session aggregation. when in doubt, the session presets docs mark which is which.
Asian range breakout — special case. the Asian range breakout endpoints use the Asia preset for start_time and end_time but take no timezone parameter — the range is interpreted in Asia/Tokyo automatically. they also take separate candle_start_time and candle_end_time parameters, which describe a daily candle window in America/New_York. if you treat these endpoints like the others and pass a timezone, it is silently ignored rather than rejected — you get the same response you would have got without it. that is worth knowing, because nothing tells you the parameter did nothing: the range is still interpreted in Asia/Tokyo, whatever you sent.
invalid market_type value
the only valid values are forex, futures, crypto, stock. anything else (including capitalized variants like STOCK or Forex) returns a 422.
invalid report slug
report slugs are case-sensitive and use dashes, not underscores or spaces. a slug that doesn't match returns a 404, not a 422 — gap_fill_standard and Gap-Fill-Standard both come back as "detail": "Not Found". one more thing that produces the same 404: the report path carries a category segment before the slug, either report_calculation or intraday_calculation, and a correct slug under the wrong one fails exactly like a misspelling. the full list of slugs, with the path each one sits under, lives in the API reference.
429 too many requests
you hit the rate limit. the response body:
{"detail": "API key rate limit exceeded"}the default rate limit is the same on every tier: 120 requests per 60-second window (sustained), a burst allowance of 30 requests per 10 seconds, and a ceiling of 1,500 requests per hour. limits are per key, and your account has one active key at a time, so in practice that's your account's limit.
what to do when you see a 429:
back off before retrying. an immediate retry will fail too.
use exponential backoff — wait 1s, then 2s, then 4s, etc. before each retry attempt.
if you're consistently hitting the limit, consider whether you actually need to be calling the API that often. caching responses for the duration of a trading day usually solves it.
rate limits are uniform across tiers — upgrading your plan doesn't raise them. if caching and backoff still aren't enough for your workload, reach out to support so we can review the use case.
about tier access
your key carries your plan. what your tier controls is which reports, tickers, lookback window, and live data you can reach, and when a request goes past that, the API tells you with a 403 and a short code. see 403 forbidden above for the list.
the full breakdown of what each tier includes is in the rate limits + tier differences.
5xx server errors
if you get a 500, 502, or 503 — that's on us, not on you. these are rare. retry with exponential backoff. if it persists for more than a few minutes, or you suspect a wider outage (multiple endpoints down at once), ping support directly with the timestamp and the last few request URLs you tried so we can correlate against backend logs.
{"detail": "internal server error"}
troubleshooting flow
if you're stuck and don't have a clear error code to work from, work through this in order:
does the request work in the docs' "try it" panel? if yes, the issue is in your code. if no, the request itself is malformed.
is the
Authorizationheader present and correctly formatted?are all required parameters present? check
start_date,end_date, and for intraday endpoints, alsostart_time,end_time,timezone.is the ticker formatted correctly for the
market_type?is the report slug spelled correctly with dashes, and does it exist in the docs catalog? a mistyped or invented slug returns 404 or 422, not data.
did the call succeed but hand back the wrong breakdown? check whether the cut you want lives on a
by-...variant rather than-standard— see 200 OK, but not the breakdown you expected above.are the dates within your tier's lookback window? essentials caps at 6 months; pro and all-access get all available history with no rolling cutoff.
is the report or ticker available in your tier? see rate limits and tier differences for the full breakdown of what each tier unlocks.
still stuck
a few last things to check:
the API base URL is
https://api.edgeful.com— make sure you're not pointing atwww.edgeful.comor anything else. v1 is served at the root of that host — there's no/v1in the path.check the docs at https://www.edgeful.com/docs for the latest endpoint catalog and parameter requirements
if your IDE or HTTP client is encoding query parameters unexpectedly (e.g., turning
:into%3A), the API will usually handle it — but it's worth testing the raw URL in curl to rule it out.deeper reference: the authentication docs cover header format, key format, and the exact error response shapes; the session presets docs cover every valid intraday session combo.
if none of that resolves it, reach out to support with the exact request URL (mask your key) and the full response body. we'll dig in.