Response Formats & Limits

FormatHeaderRow LimitBest 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

Parameter Mapping

All parameters must be lowercase. Use short codes, not full names.

Commodities

cCorn
sSoybeans
wWheat
smSoybean Meal
boSoybean Oil
ctCotton

Countries (ISO 2-letter)

cnChina
mxMexico
jpJapan
krSouth Korea
twTaiwan
coColombia

Pagination

Large result sets are paginated. Use cursor-based pagination to fetch all data.

JSON Pagination

  • Response includes has_more boolean
  • Use next_cursor for next page
  • Pass as query param: ?cursor=xxx

Arrow Pagination

  • Check header X-Has-More: true
  • Use header X-Next-Cursor
  • Pass as query param: ?cursor=xxx

Code Examples

Simple examples for JSON and Arrow responses with pagination.

Python
R
JavaScript
Java
C#
JSON
Arrow
Single (20k limit)
Paginated (no limit)
# 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)
Single (60k limit)
Paginated (no limit)
# 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
Arrow
Single (20k limit)
Paginated (no limit)
# 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)
Single (60k limit)
Paginated (no limit)
# 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
Arrow
Single (20k limit)
Paginated (no limit)
// 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;
}
Single (60k limit)
Paginated (no limit)
// 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
Arrow
Single (20k limit)
Paginated (no limit)
// 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();
}
Single (60k limit)
Paginated (no limit)
// 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
Arrow
Single (20k limit)
Paginated (no limit)
// 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;
}
Single (60k limit)
Paginated (no limit)
// 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();
}