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 300,000 rows Large datasets, analytics

Parameter Mapping

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

Commodities

cCorn
sSoybeans
wWheat (all)
w.winWinter Wheat
w.sprSpring Wheat
bBarley
sgSorghum
oOats
ryRye
sfSunflower

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#
SDK (recommended)
JSON (raw)
Arrow (raw)
# pip install fastagsdata
import fastagsdata as fdata

fdata.set_key('YOUR_API_KEY')

# Returns a pandas DataFrame. Handles pagination automatically.
df = fdata.get('wasde', comdty='c', param='stx')

# Multiple commodities and years
df = fdata.get('nass', comdty=['c', 's'], year=[2023, 2024])

# Wildcard params
df = fdata.get('nass', comdty='c', param='prg%')
Single (20k limit)
Paginated (no limit)
# JSON: 20k row limit per request
import requests, pandas as pd

r = requests.get(
    'https://api.fastagsdata.com/fundamentals',
    headers={'X-API-Key': 'YOUR_API_KEY'},
    params={'source': 'wasde', 'comdty': 'c'}
)
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 (300k limit)
Paginated (no limit)
# Arrow: 300k row limit per request
import requests, pyarrow as pa, base64, gzip
from io import BytesIO

r = requests.get(
    'https://api.fastagsdata.com/fundamentals',
    headers={
        'X-API-Key': 'YOUR_API_KEY',
        'Accept': 'application/vnd.apache.arrow.stream'
    }
)
raw = gzip.decompress(base64.b64decode(r.content))
df = pa.ipc.open_stream(BytesIO(raw)).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)
    raw = gzip.decompress(base64.b64decode(r.content))
    dfs.append(pa.ipc.open_stream(BytesIO(raw)).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/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 (300k limit)
Paginated (no limit)
# Arrow: 300k row limit per request
library(httr)
library(arrow)

resp <- GET(
    "https://api.fastagsdata.com/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/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 (300k limit)
Paginated (no limit)
// Arrow: 300k 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/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 (300k limit)
Paginated (no limit)
// Arrow: 300k 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/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 (300k limit)
Paginated (no limit)
// Arrow: 300k 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();
}