Send a file of places
For more than 100 places, send a file to /jobs: up to 100,000 places in a CSV, GeoJSON or Parquet file. Kadrin works through it in the background, and you download your file back with the numbers added. It's the same as a request in Kadrin Batch, from your own code.
Your file
Kadrin finds each place from its columns, by their usual names. Upper or lower case, spaces and underscores don't matter.
| To find each place by | Columns |
|---|---|
| Its location | Latitude and Longitude (or lat and lon) |
| Its address | Street Address, City, State and Zip Code; or one Address column with all of it |
| An official area | GEOID (the official ID number the government gives each area) |
| Your own outline | WKT (the outline written as text, in longitude and latitude) |
If your columns have other names, say which is which in the spec's place_columns, such as {"lat": "Lat (deg)", "lon": "Long (deg)"}. A column named id names each row in the results; without one, rows are numbered from 1. Or pick a column, such as {"id": "Store Name"}.
Addresses are found with the U.S. Census Bureau's geocoder. One that can't be found comes back with the reason, and costs nothing.
These samples use our example file, four places in Massachusetts. Save it as stores.csv next to your code.
With Python or JavaScript
Each script checks the file and its cost for free, sends it, waits until it's done, and saves the results.
import json
import os
import time
import requests
API = "https://api.kadrindata.com/v1"
headers = {"Authorization": f"Bearer {os.environ['KADRIN_API_KEY']}"}
spec = {
"preset": "site_report",
"around": {"type": "isochrone", "mode": "drive", "minutes": 10},
"place_columns": {"id": "Store Name"},
"keep_columns": True,
"columns": "labels",
}
def send(spec):
with open("stores.csv", "rb") as f:
r = requests.post(f"{API}/jobs", headers=headers, files={"file": f}, data={"spec": json.dumps(spec)})
r.raise_for_status()
return r.json()
# Check the file and its cost, for free
check = send({**spec, "options": {"dry_run": True}})
print(f"Read {check['places']} places for {check['estimated_credits']} credits:", check["place_columns"])
# Send it, and wait until it's done
job = send(spec)
while job["status"] in ("queued", "running"):
time.sleep(5)
job = requests.get(f"{API}/jobs/{job['id']}", headers=headers).json()
print(f"{job['areas_done']} of {job['areas_total']} places done")
# Save the results
if job["status"] != "succeeded":
raise SystemExit(f"The job {job['status']}")
with open("stores with Kadrin.csv", "wb") as out:
out.write(requests.get(job["results_url"]).content)
print("Saved stores with Kadrin.csv")
import { readFile, writeFile } from "node:fs/promises";
const API = "https://api.kadrindata.com/v1";
const headers = { Authorization: `Bearer ${process.env.KADRIN_API_KEY}` };
const spec = {
preset: "site_report",
around: { type: "isochrone", mode: "drive", minutes: 10 },
place_columns: { id: "Store Name" },
keep_columns: true,
columns: "labels",
};
async function send(spec) {
const form = new FormData();
form.append("file", new Blob([await readFile("stores.csv")]), "stores.csv");
form.append("spec", JSON.stringify(spec));
const r = await fetch(`${API}/jobs`, { method: "POST", headers, body: form });
if (!r.ok) throw new Error(await r.text());
return r.json();
}
// Check the file and its cost, for free
const check = await send({ ...spec, options: { dry_run: true } });
console.log(`Read ${check.places} places for ${check.estimated_credits} credits:`, check.place_columns);
// Send it, and wait until it's done
let job = await send(spec);
while (job.status === "queued" || job.status === "running") {
await new Promise((done) => setTimeout(done, 5000));
job = await (await fetch(`${API}/jobs/${job.id}`, { headers })).json();
console.log(`${job.areas_done} of ${job.areas_total} places done`);
}
// Save the results
if (job.status !== "succeeded") throw new Error(`The job ${job.status}`);
const results = await fetch(job.results_url);
await writeFile("stores with Kadrin.csv", Buffer.from(await results.arrayBuffer()));
console.log("Saved stores with Kadrin.csv");
With curl
These commands use jq, a small tool for reading JSON, to pick out the job's ID and status. First, check the file and its cost, for free:
curl https://api.kadrindata.com/v1/jobs \
-H "Authorization: Bearer $KADRIN_API_KEY" \
-F file=@stores.csv \
-F 'spec={"preset": "site_report", "around": {"type": "isochrone", "mode": "drive", "minutes": 10},
"place_columns": {"id": "Store Name"}, "options": {"dry_run": true}}'
The answer says how many places Kadrin read, from which columns, what it will cost, and any rows it can't use. Then send it, keeping the job's ID:
JOB=$(curl -s https://api.kadrindata.com/v1/jobs \
-H "Authorization: Bearer $KADRIN_API_KEY" \
-F file=@stores.csv \
-F 'spec={"preset": "site_report", "around": {"type": "isochrone", "mode": "drive", "minutes": 10},
"place_columns": {"id": "Store Name"}, "keep_columns": true, "columns": "labels"}' | jq -r .id)
echo "$JOB"
Wait until it's done:
while curl -s "https://api.kadrindata.com/v1/jobs/$JOB" -H "Authorization: Bearer $KADRIN_API_KEY" \
| jq -e '.status == "queued" or .status == "running"' > /dev/null; do
sleep 5
done
Then save the results:
URL=$(curl -s "https://api.kadrindata.com/v1/jobs/$JOB" -H "Authorization: Bearer $KADRIN_API_KEY" | jq -r .results_url)
curl -s -o "stores with Kadrin.csv" "$URL"
Your results
You get your file back with Kadrin's columns after your own (keep_columns), named in plain words ("columns": "labels"), such as "Median household income (dollars)"; leave columns out for measure IDs instead. Each measure has its margin of error where it has one, and the comparisons a preset adds. A place that failed has its reason in the error columns, and costs nothing.
- The results link works for 7 days.
- Instead of checking, you can give a
webhook_url: a web address on your side that we call when the job is done. DELETE /jobs/{id}cancels a job. You pay only for the places already done, and can still download them.
Jobs in the API reference has every field.