Socrata Open Data API (SODA)
Document information
| Field | Value |
|---|---|
| Canonical URL | /docs/11_partner_services/50_socrata |
| Version (published date) | 2026-08-14 |
| Tags | partners, socrata, soda, open-data, restapi-generic |
Summary
The Socrata Open Data API (SODA), now part of Tyler Technologies Data & Insights, is how governments, non-profits, and NGOs publish tabular and geospatial open data as HTTP APIs. Every public dataset has an endpoint. You query with SoQL (a SQL-like language on GET parameters, or a JSON body on SODA 3). Tealfabric has exercised SODA 2.x GET /resource/{id}.json against restapi-generic-1.1.0 (operation: receive, method: GET).
Reading public datasets is free. An application token is optional but raises throttling (about 1,000 requests per rolling hour). Tealfabric does not issue tokens. Publishing (upsert, catalog write) is for Tyler/Socrata publishers, not a typical tenant workflow.
Why use it with Tealfabric
Tealfabric stores your entities and processes. It does not host city or national open-data catalogs. Use SODA when a workflow needs a public register that already lives on a Socrata portal:
- Business licenses, building permits, inspections, or crime tables from a city/state site
- Geospatial points (facilities, fuel stations) for a WebApp map
- A scheduled sync of rows newer than a DataPool watermark
- Discovery of which portal and dataset identifier to call
Typical pattern: find the dataset on the Open Data Network or the portal /browse page, copy the eight-character identifier (xxxx-xxxx), receive pages into DataPool, and join later to your entities (for example a local license number next to a PRH or OpenCorporates record). Dataset licences belong to the data owner, not to Tyler’s developer-docs licence.
What the API provides
There is no single global data host. Each catalog is a domain such as data.cityofchicago.org or soda.demo.socrata.com. The dataset identifier is four alphanumerics, a dash, and four more (ydr8-5enu).
| Surface | URL pattern | Role |
|---|---|---|
| SODA 2.x resource (recommended for Tealfabric) | GET https://{domain}/resource/{id}.json | Rows as a JSON array. SoQL via $select, $where, $order, $group, $q, $limit, $offset |
| SODA 3 query | POST https://{domain}/api/v3/views/{id}/query.json | JSON body { query, page }. Token or user auth required. Prefer POST for long queries |
| SODA 3 export | POST …/export.csv (and other formats) | Full-file export, not a paging API |
| Discovery / catalog | GET https://api.us.socrata.com/api/catalog/v1 | Search metadata across portals (q, domains, limit, order) |
SoQL is SQL-shaped. Simple equality can also be a query parameter named after the column (status=ISSUED). Paging on 2.x: default page is 1,000 rows; $limit max is 50,000 on 2.0 (2.1 has a higher ceiling). Use $offset for the next page. Always pair $order with paging so rows do not shift between calls.
restapi-generic-1.1.0 receive treats a JSON array as data directly (no extra { results } wrapper). SODA 2.x .json responses are arrays of row objects.
Application token: register in a Socrata profile, send X-App-Token (preferred) or $$app_token / app_token on older versions. Unauthenticated calls share an IP throttle pool and will 429 sooner. Tokens identify the app for throttling; they are not the same as publisher login. Use HTTPS so the token is not copied.
SODA 3 wants a token (or user auth) on every query. Docs: getting started, endpoints, SoQL, application tokens. Platform status: status.socrata.com.
Platform compatibility
Create one GET integration per Socrata domain you call (the host is part of url). Keep GET / receive. SODA 3 POST is a separate integration with method: POST and operation: send if you need it.
Dataset rows (SODA 2.x)
| Integration setting | Value |
|---|---|
| Connector | restapi-generic-1.1.0 |
url | https://soda.demo.socrata.com (replace with the portal host) |
path | /resource/4tka-6guv.json (replace with the dataset id) |
method | GET |
auth_header_name | X-App-Token |
api_key | your application token (optional but recommended; no Bearer prefix) |
accept | application/json |
| Process step operation | receive |
Catalog search (Discovery)
| Integration setting | Value |
|---|---|
| Connector | restapi-generic-1.1.0 |
url | https://api.us.socrata.com |
path | /api/catalog/v1 |
method | GET |
auth_header_name | X-App-Token (optional) |
| Process step operation | receive |
Do not point restapi-generic-1.1.0 at producer upsert/delete unless you are that catalog’s publisher and have a dedicated write integration. Leave executable_by_ai_agents off until you accept throttle spend. Put the dataset’s SODA docs URL in the integration description.
Connector details: Generic REST API connector.
Connect from a process step
Replace the integration ID. The example pages a SODA 2.x resource until a short page.
const integrationId = "YOUR_SOCRATA_INTEGRATION_ID";
const PAGE_SIZE = 1000;
function failMessage(result: Record<string, unknown>, fallback: string): string {
const err = result.error;
if (typeof err === "string") return err;
if (err && typeof err === "object") {
return String((err as Record<string, unknown>).message ?? fallback);
}
return fallback;
}
function unwrapRows(result: Record<string, unknown>): unknown[] {
const envelope = (result.data ?? {}) as Record<string, unknown>;
const rows = envelope.data;
return Array.isArray(rows) ? rows : [];
}
const rows: unknown[] = [];
let offset = 0;
while (true) {
const result = (await integration.executeSync(integrationId, {
operation: "receive",
query: {
$limit: String(PAGE_SIZE),
$offset: String(offset),
$order: ":id",
},
})) as Record<string, unknown>;
if (!result?.success) {
throw new Error(failMessage(result, "SODA receive failed"));
}
const page = unwrapRows(result);
if (page.length === 0) {
break;
}
rows.push(...page);
if (page.length < PAGE_SIZE) {
break;
}
offset += PAGE_SIZE;
}
return { success: true, data: { rows, row_count: rows.length } };Filter in SoQL ($where is a GET query parameter, not a request body):
await integration.executeSync(integrationId, {
operation: "receive",
query: {
$select: "station_name,city,fuel_type_code",
$where: "city = 'Chicago'",
$limit: "100",
},
});
Find a dataset, then point a second integration at that domain and id:
await integration.executeSync(discoveryIntegrationId, {
operation: "receive",
query: {
q: "building permits",
domains: "data.cityofchicago.org",
only: "dataset",
limit: "10",
},
});
SODA 3 (only if you need POST / long queries): method: POST, operation: send, path /api/v3/views/{id}/query.json, body { query: "SELECT *", page: { pageNumber: 1, pageSize: 1000 } }. Unwrap result.data.response.
Example use cases with Tealfabric
Cache a public register. A scheduled ProcessFlow pages a license or permit dataset into DataPool, keyed by the portal’s row identifier (:id). WebApps query DataPool instead of SODA on every form load.
Watermark sync. $where on an updated timestamp column ($where=updated_at > '2026-08-01T00:00:00') plus $order so you only pull rows newer than the last successful run.
Local license + legal entity. Join a city business-license table to OpenCorporates or PRH BIS on name or company number. SODA supplies the local permit; the register API supplies the legal entity.
Map WebApp. Select Point / lat-long columns, store them, and render in a WebApp. Request GeoJSON only if you change accept and parse that format.
Investigator search. Discovery q= to list candidate datasets; then a SODA 2.x receive with $q or $where for the UI. Respect the data owner’s terms.
Agent lookup. Enable executable_by_ai_agents on a read integration whose description names the exact dataset and SoQL columns, so Trace AI does not guess identifiers.
Pricing and accounts
| Use | Cost |
|---|---|
| GET public dataset rows (consumer SODA) | Free. Throttle is higher with a free application token (~1,000 requests / rolling hour). Ask Tyler/Socrata support for a higher cap. |
| Discovery catalog search | Free |
| Publish / upsert / private assets | Tyler Data & Insights publisher contract — not a Tealfabric add-on |
There is no Tealfabric SKU. Tokens come from a Socrata developer profile. If another party copies your token, their traffic counts against your throttle.
Limits to keep in mind
- One integration
urlper portal host. Dataset id lives inpathor in thepathargument ofexecuteSync. - SODA 2.x: GET /
receive, SoQL inquery($limit,$offset,$where, …). - SODA 3: POST /
sendif you use it; token required. - Default 1,000 rows; page with
$offset. Do not omit$orderwhen paging. - HTTP 429 when throttled. Back off. Use
X-App-Token. - Do not send the token to the browser.
- Do not upsert unless you are the publisher.
- Attribute the data owner. Confirm each dataset’s licence on the portal.