Pagination
Tapetide never truncates a response to fit. When a result is larger than one page, the response says so and gives you the exact value to ask for the next page.
Cursor pagination (collections)
Section titled “Cursor pagination (collections)”Collection endpoints return an array in data, paging fields in meta, and a ready-made next URL in links:
{ "data": [ { "date": "2026-10-20", "name": "Diwali Laxmi Pujan", "exchange": "NSE", "type": "FULL" } ], "meta": { "as_of": "2026-10-16T09:30:00.000Z", "route": "listMarketHolidays", "count": 1, "limit": 100, "has_more": true, "next_cursor": "eyJkIjoiMjAyNi0xMC0yMCJ9" }, "links": { "next": "/v1/market/holidays?from=2026-01-01&cursor=eyJkIjoiMjAyNi0xMC0yMCJ9" }}| Field | Meaning |
|---|---|
meta.count |
Items in this page. |
meta.limit |
The page size that was applied. |
meta.has_more |
true when another page exists. |
meta.next_cursor |
Opaque string for the next page, or null on the last page. |
links.next |
Path and query for the next page with your filters kept, or null. Prefix it with https://api.tapetide.com. |
The rules
Section titled “The rules”limitdefaults per endpoint (shown in the reference) and has a per-endpoint maximum. A value above the maximum is rejected with400 invalid_request. It is never silently clamped.cursoris opaque. Passmeta.next_cursorback unchanged, with the same filters. A cursor the API did not issue returns400 invalid_requestwithdetails.param = "cursor".- Paging is keyset, not offset, so pages stay stable while new rows arrive at the other end of the range.
- Each page is one request and costs one unit.
- Unknown query parameters are rejected with a
400that lists the accepted ones, so a typo never silently returns unfiltered data.
Follow every page
Section titled “Follow every page”import os, requests
BASE = "https://api.tapetide.com"s = requests.Session()s.headers["Authorization"] = f"Bearer {os.environ['TAPETIDE_TOKEN']}"
url = "/v1/market/holidays?from=2020-01-01&limit=500"rows = []while url: body = s.get(BASE + url, timeout=30).json() rows.extend(body["data"]) url = body["links"]["next"]print(len(rows))let url = "/v1/market/holidays?from=2020-01-01&limit=500";const rows = [];while (url) { const res = await fetch(`https://api.tapetide.com${url}`, { headers: { Authorization: `Bearer ${process.env.TAPETIDE_TOKEN}` }, }); const body = await res.json(); rows.push(...body.data); url = body.links.next;}Window pagination (price history)
Section titled “Window pagination (price history)”/v1/stocks/{symbol}/price serves at most 2,000 calendar days per call. It reports the window it actually served in meta.window:
"window": { "requested_days": 2000, "served_days": 2000, "max_days": 2000, "truncated": false, "first_date": "2021-04-26", "last_date": "2026-10-14", "has_more": true, "next_cursor": "2021-04-25"}To page further back, pass meta.window.next_cursor as end_date on the next call and repeat until has_more is false. Always read truncated: when it is true you asked for more days than one call serves.
Bounded lists without a cursor
Section titled “Bounded lists without a cursor”Some older routes (for example /v1/stocks/{symbol}/events, /deals, /v1/market/news) return the most recent N items with a limit parameter and no cursor. The reference states the default and maximum for each.