Skip to main content

JavaScript Examples

These examples use the native fetch API available in Node.js 18+ and all modern browsers. No additional dependencies required.

Store your API key in an environment variable — never hardcode it in client-side code:

# Node.js
export PRAIRIECLOUD_API_KEY="pck_live_YOUR_KEY_HERE"
Browser usage

Do not include your API key in browser-side JavaScript. Your key would be visible to anyone who inspects your page source. Route API calls through your own backend.


Setup

const API_BASE = "https://api.prairiecloud.io";
const API_KEY = process.env.PRAIRIECLOUD_API_KEY;

const defaultHeaders = {
"X-API-Key": API_KEY,
"Content-Type": "application/json",
};

// Helper: build query string from params object
function buildUrl(path, params = {}) {
const url = new URL(`${API_BASE}${path}`);
Object.entries(params).forEach(([k, v]) => {
if (v !== undefined && v !== null) {
url.searchParams.set(k, String(v));
}
});
return url.toString();
}

Single Variable Lookup

const response = await fetch(
buildUrl("/v1/data", {
variables: "pop_total",
geo: "state:48",
}),
{ headers: defaultHeaders }
);

if (!response.ok) {
const error = await response.json();
throw new Error(`API error ${error.status}: ${error.detail}`);
}

const result = await response.json();
const geoData = result.data["state:48"];
const population = geoData.variables.pop_total.estimate;

console.log(`${geoData.name}: ${population.toLocaleString()}`);
// → Texas: 30,029,572

Multi-Variable Query

const response = await fetch(
buildUrl("/v1/data", {
variables: "pop_total,income_median_household",
geo: "state:48,state:06,state:36",
vintage: 2023,
}),
{ headers: defaultHeaders }
);

if (!response.ok) throw new Error((await response.json()).detail);

const { data } = await response.json();

Object.entries(data).forEach(([geoKey, geoData]) => {
const pop = geoData.variables.pop_total.estimate;
const income = geoData.variables.income_median_household.estimate;
console.log(
`${geoData.name}: pop=${pop?.toLocaleString()}, income=$${income?.toLocaleString()}`
);
});

Browse the Variable Catalog

async function searchVariables(keyword, limit = 20) {
const response = await fetch(
buildUrl("/v1/variables", { search: keyword, limit }),
{ headers: defaultHeaders }
);

if (!response.ok) throw new Error((await response.json()).detail);

const { data } = await response.json();
return data;
}

const incomeVars = await searchVariables("income");
incomeVars.forEach((v) => {
console.log(`${v.api_name}${v.label} (${v.unit})`);
});

Paginating the Variable Catalog

async function getAllVariables() {
const allVars = [];
let offset = 0;
const limit = 200;

while (true) {
const response = await fetch(
buildUrl("/v1/variables", { limit, offset }),
{ headers: defaultHeaders }
);

if (!response.ok) throw new Error((await response.json()).detail);

const payload = await response.json();
allVars.push(...payload.data);

const total = payload.meta.total_count;
offset += payload.data.length;

if (offset >= total) break;
}

return allVars;
}

const allVars = await getAllVariables();
console.log(`Total variables: ${allVars.length}`);

Error Handling

async function apiRequest(path, params = {}) {
const response = await fetch(buildUrl(path, params), {
headers: defaultHeaders,
});

if (response.ok) {
return await response.json();
}

let problem;
try {
problem = await response.json();
} catch {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}

switch (response.status) {
case 401:
throw new Error(`Authentication failed: ${problem.detail}`);
case 404:
throw new Error(`Not found: ${problem.detail}`);
case 429: {
const retryAfter = response.headers.get("Retry-After") ?? "60";
throw new Error(`Rate limited. Retry after ${retryAfter}s. Request ID: ${problem.request_id}`);
}
default:
throw new Error(`API error ${problem.status}: ${problem.detail} (request_id: ${problem.request_id})`);
}
}

Retry with Exponential Backoff

async function fetchWithRetry(path, params = {}, maxRetries = 5) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(buildUrl(path, params), {
headers: defaultHeaders,
});

if (response.status === 429) {
const retryAfter = parseInt(response.headers.get("Retry-After") ?? "1", 10);
const jitter = Math.random();
const wait = (retryAfter + jitter) * 1000;
console.warn(`Rate limited. Waiting ${(wait / 1000).toFixed(1)}s…`);
await new Promise((r) => setTimeout(r, wait));
continue;
}

if (response.status >= 500) {
const wait = (2 ** attempt + Math.random()) * 1000;
console.warn(`Server error ${response.status}. Waiting ${(wait / 1000).toFixed(1)}s…`);
await new Promise((r) => setTimeout(r, wait));
continue;
}

if (!response.ok) {
const error = await response.json();
throw new Error(`${error.status}: ${error.detail}`);
}

return await response.json();
}

throw new Error(`Request failed after ${maxRetries} retries`);
}

Check Your Usage

const response = await fetch(
`${API_BASE}/v1/account/usage`,
{ headers: defaultHeaders }
);

const { data: usage } = await response.json();

console.log(`Plan: ${usage.tier}`);
console.log(`Requests: ${usage.quota_used.toLocaleString()} / ${usage.quota_limit?.toLocaleString() ?? "unlimited"}`);
console.log(`Remaining: ${usage.quota_remaining?.toLocaleString() ?? "unlimited"}`);
console.log(`Resets: ${usage.period_end}`);

Next Steps