Simple REST API with JSON and Arrow responses.
| Format | Header | Row Limit | Best For |
|---|---|---|---|
| JSON | Accept: application/json (default) |
20,000 rows | Quick queries, web apps |
| Arrow | Accept: application/vnd.apache.arrow.stream |
60,000 rows | Large datasets, analytics |
All parameters must be lowercase. Use short codes, not full names.
| c | Corn |
| s | Soybeans |
| w | Wheat |
| sm | Soybean Meal |
| bo | Soybean Oil |
| ct | Cotton |
| cn | China |
| mx | Mexico |
| jp | Japan |
| kr | South Korea |
| tw | Taiwan |
| co | Colombia |
Large result sets are paginated. Use cursor-based pagination to fetch all data.
has_more booleannext_cursor for next page?cursor=xxxX-Has-More: trueX-Next-Cursor?cursor=xxxSimple examples for JSON and Arrow responses with pagination.
# JSON: 20k row limit per request import requests, pandas as pd r = requests.get( 'https://api.fastagsdata.com/v1/fundamentals', headers={'X-API-Key': 'YOUR_API_KEY'}, params={'comdty': 'c', 'country': 'cn'} ) df = pd.DataFrame(r.json()['data'])
# JSON Paginated: no row limit dfs, cursor = [], None while True: params = {'cursor': cursor} if cursor else {} r = requests.get(URL, headers=HEADERS, params=params) data = r.json() dfs.append(pd.DataFrame(data['data'])) if not data['has_more']: break cursor = data['next_cursor'] df = pd.concat(dfs)
# Arrow: 60k row limit per request import requests, pyarrow as pa from io import BytesIO r = requests.get( 'https://api.fastagsdata.com/v1/fundamentals', headers={ 'X-API-Key': 'YOUR_API_KEY', 'Accept': 'application/vnd.apache.arrow.stream' } ) df = pa.ipc.open_stream(BytesIO(r.content)).read_all().to_pandas()
# Arrow Paginated: no row limit dfs, cursor = [], None while True: params = {'cursor': cursor} if cursor else {} r = requests.get(URL, headers=ARROW_HEADERS, params=params) dfs.append(pa.ipc.open_stream(BytesIO(r.content)).read_all().to_pandas()) if r.headers.get('X-Has-More') != 'true': break cursor = r.headers.get('X-Next-Cursor') df = pd.concat(dfs)
# JSON: 20k row limit per request library(httr) library(jsonlite) resp <- GET( "https://api.fastagsdata.com/v1/fundamentals", add_headers("X-API-Key" = "YOUR_API_KEY"), query = list(comdty = "c", country = "cn") ) df <- fromJSON(content(resp, "text"))$data
# JSON Paginated: no row limit dfs <- list() cursor <- NULL repeat { query <- if (!is.null(cursor)) list(cursor = cursor) else list() resp <- GET(url, add_headers("X-API-Key" = api_key), query = query) data <- fromJSON(content(resp, "text")) dfs <- append(dfs, list(data$data)) if (!data$has_more) break cursor <- data$next_cursor } df <- do.call(rbind, dfs)
# Arrow: 60k row limit per request library(httr) library(arrow) resp <- GET( "https://api.fastagsdata.com/v1/fundamentals", add_headers( "X-API-Key" = "YOUR_API_KEY", "Accept" = "application/vnd.apache.arrow.stream" ) ) df <- read_ipc_stream(content(resp, "raw"))
# Arrow Paginated: no row limit dfs <- list() cursor <- NULL repeat { query <- if (!is.null(cursor)) list(cursor = cursor) else list() resp <- GET(url, add_headers("X-API-Key" = api_key, "Accept" = "application/vnd.apache.arrow.stream"), query = query) dfs <- append(dfs, list(read_ipc_stream(content(resp, "raw")))) if (resp$headers$"x-has-more" != "true") break cursor <- resp$headers$"x-next-cursor" } df <- do.call(rbind, dfs)
// JSON: 20k row limit per request const resp = await fetch( 'https://api.fastagsdata.com/v1/fundamentals?comdty=c&country=cn', { headers: { 'X-API-Key': 'YOUR_API_KEY' } } ); const { data } = await resp.json();
// JSON Paginated: no row limit let allData = [], cursor = null; while (true) { const url = cursor ? `${BASE_URL}?cursor=${cursor}` : BASE_URL; const r = await fetch(url, { headers: { 'X-API-Key': API_KEY } }); const json = await r.json(); allData.push(...json.data); if (!json.has_more) break; cursor = json.next_cursor; }
// Arrow: 60k row limit per request import { tableFromIPC } from 'apache-arrow'; const resp = await fetch(BASE_URL, { headers: { 'X-API-Key': 'YOUR_API_KEY', 'Accept': 'application/vnd.apache.arrow.stream' } }); const table = tableFromIPC(await resp.arrayBuffer());
// Arrow Paginated: no row limit let tables = [], cursor = null; while (true) { const url = cursor ? `${BASE_URL}?cursor=${cursor}` : BASE_URL; const r = await fetch(url, { headers: ARROW_HEADERS }); tables.push(tableFromIPC(await r.arrayBuffer())); if (r.headers.get('X-Has-More') !== 'true') break; cursor = r.headers.get('X-Next-Cursor'); }
// JSON: 20k row limit per request HttpClient client = HttpClient.newHttpClient(); HttpRequest req = HttpRequest.newBuilder() .uri(URI.create("https://api.fastagsdata.com/v1/fundamentals?comdty=c")) .header("X-API-Key", "YOUR_API_KEY") .build(); HttpResponse<String> resp = client.send(req, BodyHandlers.ofString()); // Parse with Jackson/Gson
// JSON Paginated: no row limit List<JsonNode> allData = new ArrayList<>(); String cursor = null; while (true) { String url = cursor != null ? baseUrl + "?cursor=" + cursor : baseUrl; HttpResponse<String> resp = client.send(buildReq(url), BodyHandlers.ofString()); JsonNode json = mapper.readTree(resp.body()); allData.addAll(json.get("data")); if (!json.get("has_more").asBoolean()) break; cursor = json.get("next_cursor").asText(); }
// Arrow: 60k row limit per request HttpRequest req = HttpRequest.newBuilder() .uri(URI.create(baseUrl)) .header("X-API-Key", apiKey) .header("Accept", "application/vnd.apache.arrow.stream") .build(); HttpResponse<byte[]> resp = client.send(req, BodyHandlers.ofByteArray()); try (ArrowStreamReader reader = new ArrowStreamReader( new ByteArrayInputStream(resp.body()), new RootAllocator())) { while (reader.loadNextBatch()) { /* process */ } }
// Arrow Paginated: no row limit List<VectorSchemaRoot> batches = new ArrayList<>(); String cursor = null; while (true) { String url = cursor != null ? baseUrl + "?cursor=" + cursor : baseUrl; HttpResponse<byte[]> resp = client.send(buildArrowReq(url), BodyHandlers.ofByteArray()); try (ArrowStreamReader r = new ArrowStreamReader(...)) { while (r.loadNextBatch()) batches.add(r.getVectorSchemaRoot()); } if (!"true".equals(resp.headers().firstValue("X-Has-More").orElse(""))) break; cursor = resp.headers().firstValue("X-Next-Cursor").orElse(null); }
// JSON: 20k row limit per request var client = new HttpClient(); client.DefaultRequestHeaders.Add("X-API-Key", "YOUR_API_KEY"); var resp = await client.GetStringAsync( "https://api.fastagsdata.com/v1/fundamentals?comdty=c" ); var data = JsonSerializer.Deserialize<ApiResponse>(resp);
// JSON Paginated: no row limit var allData = new List<DataRow>(); string cursor = null; while (true) { var url = cursor != null ? $"{baseUrl}?cursor={cursor}" : baseUrl; var json = JsonSerializer.Deserialize<ApiResponse>(await client.GetStringAsync(url)); allData.AddRange(json.Data); if (!json.HasMore) break; cursor = json.NextCursor; }
// Arrow: 60k row limit per request var req = new HttpRequestMessage(HttpMethod.Get, baseUrl); req.Headers.Add("Accept", "application/vnd.apache.arrow.stream"); var resp = await client.SendAsync(req); var stream = await resp.Content.ReadAsStreamAsync(); using var reader = new ArrowStreamReader(stream); while (await reader.ReadNextRecordBatchAsync() is RecordBatch batch) { }
// Arrow Paginated: no row limit var batches = new List<RecordBatch>(); string cursor = null; while (true) { var url = cursor != null ? $"{baseUrl}?cursor={cursor}" : baseUrl; var resp = await client.SendAsync(BuildArrowRequest(url)); using var reader = new ArrowStreamReader(await resp.Content.ReadAsStreamAsync()); while (await reader.ReadNextRecordBatchAsync() is RecordBatch b) batches.Add(b); if (resp.Headers.GetValues("X-Has-More").FirstOrDefault() != "true") break; cursor = resp.Headers.GetValues("X-Next-Cursor").FirstOrDefault(); }