> For the complete documentation index, see [llms.txt](https://docs.nexus.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nexus.xyz/api-reference/candles/fetch-o-h-l-c-v.md).

# GET /markets/{market\_id}/candles

Get OHLCV candles

Get OHLCV candles.

`operationId`: `fetchOHLCV` · CCXT method: `fetchOHLCV`

> **Declared server:** `https://api.testnet.nexus.xyz/v1`. See [Networks](/api-reference/guides/networks.md).

**Authentication:** none. This endpoint is public.

## Get OHLCV candles

> Returns candlestick data as arrays: \[timestamp, open, high, low, close, volume], ascending by timestamp. Optionally bounded by \`startTime\` / \`endTime\`; unbounded, it returns the latest \`limit\` bars.\
> \
> \*\*\`startTime\` sets the paging direction.\*\* With \`startTime\` given, the \*\*earliest\*\* \`limit\` bars at or after it are returned, so the standard \`ccxt.fetchOHLCV\` loop that advances \`since\` progresses: each request starts where the last one ended. Without \`startTime\` — the unbounded or \`endTime\`-only case — the \*\*most recent\*\* \`limit\` bars in the window are returned, which is what "the latest bars" means above and is unchanged. To reconstruct a long history, pass \`startTime\` and advance it by the timestamp after the last bar received. This operation has no pagination cursor.

```json
{"openapi":"3.1.0","info":{"title":"Nexus Exchange API","version":"0.9.101"},"tags":[{"name":"Candles","description":"OHLCV candlestick data"}],"servers":[{"url":"https://api.testnet.nexus.xyz/v1","description":"Public testnet (play funds) — the default server and /v1 transport base for unversioned paths."},{"url":"https://api.nexus.xyz/v1","description":"Mainnet — **REAL FUNDS**. Collateral is USDX bridged from Ethereum Mainnet; orders placed here move real money, and there is no faucet. The durable per-network base. Listed after testnet on purpose, so nothing that reaches for \"the first https server\" lands on the real-funds target. Not resolvable yet: DNS/TLS is a separate infra change."},{"url":"http://localhost:9090","description":"Local development — the indexer served directly. Not a public network; whatever your local instance holds."}],"security":[],"paths":{"/markets/{market_id}/candles":{"get":{"tags":["Candles"],"summary":"Get OHLCV candles","operationId":"fetchOHLCV","description":"Returns candlestick data as arrays: [timestamp, open, high, low, close, volume], ascending by timestamp. Optionally bounded by `startTime` / `endTime`; unbounded, it returns the latest `limit` bars.\n\n**`startTime` sets the paging direction.** With `startTime` given, the **earliest** `limit` bars at or after it are returned, so the standard `ccxt.fetchOHLCV` loop that advances `since` progresses: each request starts where the last one ended. Without `startTime` — the unbounded or `endTime`-only case — the **most recent** `limit` bars in the window are returned, which is what \"the latest bars\" means above and is unchanged. To reconstruct a long history, pass `startTime` and advance it by the timestamp after the last bar received. This operation has no pagination cursor.","parameters":[{"$ref":"#/components/parameters/MarketId"},{"name":"timeframe","in":"query","schema":{"type":"string","enum":["1s","1m","5m","1h"],"default":"1m"}},{"$ref":"#/components/parameters/CandleStartTime"},{"$ref":"#/components/parameters/CandleEndTime"},{"name":"limit","in":"query","description":"Maximum number of bars to return. A value above the maximum is clamped rather than rejected.\n\nThe maximum is what the server accepts, not a promise of how many bars exist: the effective ceiling depends on the history available for that market and timeframe, so a response may hold fewer bars than requested. Read `x-nexus-candles-truncated` to tell a capped page from a complete one.","schema":{"type":"integer","default":200,"maximum":5000}}],"responses":{"200":{"description":"OHLCV candles for the market.","content":{"application/json":{"schema":{"type":"array","items":{"type":"array","prefixItems":[{"type":"integer","title":"timestamp","description":"timestamp (ms)"},{"type":"number","title":"open","description":"open"},{"type":"number","title":"high","description":"high"},{"type":"number","title":"low","description":"low"},{"type":"number","title":"close","description":"close"},{"type":"number","title":"volume","description":"volume"}]}}}},"headers":{}},"400":{"$ref":"#/components/responses/InvalidCandleQuery"},"429":{"$ref":"#/components/responses/RateLimited"}}}}},"components":{"parameters":{"MarketId":{"name":"market_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Z0-9]+(-[A-Z0-9]+)*$","maxLength":64},"description":"Market identifier (e.g. BTC-USDX-PERP, ETH-USDX-PERP). Uppercase ASCII alphanumeric segments joined by single hyphens, at most 64 characters. A value outside that set is rejected with `400` (`INVALID_MARKET_ID`) before any store is read — it cannot name a market, so an empty result would assert that nothing traded rather than that the question was malformed. A well-formed identifier that names no listed market is a `404`, not a `400`."},"CandleStartTime":{"name":"startTime","in":"query","required":false,"schema":{"type":"integer","format":"int64","minimum":0},"description":"Inclusive lower bound of the window, Unix milliseconds UTC, compared against each candle's bucket start. Omit for no lower bound.\n\n**Supplying `startTime` also sets the paging direction.** With it, the response begins AT this bound and runs forward, so a capped page is the OLDEST bars in the window and its last bar is where the next request resumes — `ccxt.fetchOHLCV`'s `since`, whose standard loop advances from the last bar it was handed. Without it, the response is the NEWEST bars in the window, which is what `fetchOHLCV` with no `since` means.\n\nThis is a correctness property, not a preference: served newest-first, a `since` deep in history would return the bars adjacent to now, the caller's cursor would jump to now, and the loop would exit after one page — reporting a complete history with everything in between missing. No error and no empty page, just a gap shaped like an answer.\n\nMust be strictly before `endTime` when both are given; an inverted window is rejected with `400` (`INVALID_RANGE`) rather than normalised, so a nonsensical window never reads back as an empty one. A negative value is rejected with `400` (`INVALID_TIME`).\n\nSpelled in camelCase rather than the snake_case used elsewhere in this API, deliberately: `ccxt.fetchOHLCV` and the Binance and Hyperliquid kline contracts all use these names."},"CandleEndTime":{"name":"endTime","in":"query","required":false,"schema":{"type":"integer","format":"int64","minimum":0},"description":"Inclusive upper bound of the window, Unix milliseconds UTC, compared against each candle's bucket start. Omit to read up to now.\n\nWith no `startTime`, the operation returns the newest bars in the window, so a long history can be walked by moving `endTime` backwards. Prefer paging forward on `startTime` instead — that is the direction `ccxt.fetchOHLCV` pages in, and see that parameter for why. `endTime` alone does not change the direction.\n\nSee `startTime` for the rejection rules and for why both are camelCase."}},"responses":{"InvalidCandleQuery":{"description":"The candle query was rejected before any data was read. Branch on `code`:\n\n| code | cause |\n|---|---|\n| `INVALID_TIMEFRAME` | `timeframe` is not one of the supported values |\n| `INVALID_TIME` | `startTime` or `endTime` was negative |\n| `INVALID_RANGE` | `startTime` was not strictly before `endTime` |\n\nEach is rejected rather than coerced. Serving a nearby timeframe, or silently swapping an inverted window, would return a confident answer to a question that was not asked. `message` is a human-readable diagnostic and its wording is not stable — do not match on it.","content":{"application/json":{}}},"RateLimited":{"description":"Rate limit exceeded. **Retryable** — honour `retry-after` and back off; this is what separates a `429` from the permanent `403` jurisdiction refusals. Branch on the body's `code`, which is always `RATE_LIMIT_EXCEEDED`, and on `bucket` to learn WHICH budget bottlenecked — the buckets are independent, so a caller at its read ceiling can still submit orders and should not back off the whole client. Do not parse `message`: it is a diagnostic whose wording is not stable. Requests are charged by weight, so exhausting a budget takes fewer heavy calls than `x-ratelimit-limit` suggests. See “Rate limits” in the API description.","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Budget of request weight per second for your tier — not a count of requests. Sent on successful responses too."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Tokens left, in unit-cost requests: 10 means ten weight-1 requests or two weighing 5. Sent on successful responses too. Always `0` on this response."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix timestamp (seconds) when the budget is whole again. Sent only on a `429`, never on a success."},"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying, never below 1. Derived from the **weighted** cost of the refused request, so it is not in the same unit as `X-RateLimit-Remaining`. Sent only on a `429`."},"X-RateLimit-Bucket":{"schema":{"type":"string","enum":["key","owner","order","cancel","ip","login"]},"description":"Which budget refused this request, mirroring the body's `bucket`. `key` — the per-API-key bucket; `owner` — the per-account request bucket; `order` — the trading-action bucket, order submission and amend only; `cancel` — the separate cancellation bucket, `DELETE` on the order surface only; `ip` — the public per-IP bucket for unauthenticated and gateway traffic; `login` — the dedicated per-IP bucket for `POST /auth/login`, tighter than `ip` and drawn from its own pool, so a login refusal leaves the read budget untouched and vice versa. The read, trading and cancellation budgets are counted separately, so a `429` on one does not mean the others are exhausted — in particular a `429` naming `order` never means your cancels are refused. Sent only on a `429`."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}}},"schemas":{"RateLimitError":{"type":"object","description":"The body of a 429. Branch on `code` (always `RATE_LIMIT_EXCEEDED`) and on `bucket` to learn WHICH budget bottlenecked. Schematised rather than left to `examples` because `bucket` is meant to be read by machines: an example gives a generated client no field and a validator nothing to check `\"bucket\": \"ownr\"` against. `bucket` here and the `X-RateLimit-Bucket` header carry the same value and the same enum.","required":["code","message","bucket"],"properties":{"code":{"type":"string","enum":["RATE_LIMIT_EXCEEDED"],"description":"Always `RATE_LIMIT_EXCEEDED` on this response. The other 429s this API can return carry different codes (`too_many_agents`, `rate_limited`, `cap_exceeded`, `daily_limit_exceeded`) and are not this shape."},"message":{"type":"string","description":"A diagnostic whose wording is NOT stable. Do not parse it; branch on `bucket`."},"bucket":{"type":"string","enum":["key","owner","order","cancel","ip","login"],"description":"Which budget refused this request. `key` — the per-API-key bucket; `owner` — the per-account request bucket; `order` — the trading-action bucket, order submission and amend only; `cancel` — the separate cancellation bucket, `DELETE` on the order surface only; `ip` — the public per-IP bucket for unauthenticated and gateway traffic; `login` — the dedicated per-IP bucket for `POST /auth/login`, tighter than `ip` and drawn from its own pool, so a login refusal leaves the read budget untouched and vice versa. The buckets are independent, so a caller at its read ceiling can still submit orders, and a caller at its submission ceiling can still cancel — do not back off the whole client on any one of them."},"tier":{"type":"string","description":"The rate-limit tier the refused request was charged against — `public` for the per-IP bucket, otherwise the authenticated key's tier. Absent on responses from paths that refuse before a tier is resolved."}}}}}}
```

***

<sub>API version</sub> <sub></sub><sub>`0.9.131`</sub>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.nexus.xyz/api-reference/candles/fetch-o-h-l-c-v.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
