State & Local Data Dictionary¶
This document describes the fields available on the SLED API endpoints (/api/sled/opportunities/, /api/sled/forecasts/, and the nested revisions route). For filtering, ordering, and pagination, see the State & Local API Reference.
Overview¶
SLED is state, local and education procurement — solicitations that never appear on SAM.gov because they were never federal. There is no national feed; every jurisdiction publishes on its own portal, and Tango reads them one at a time. One row is one advertised solicitation, arbitrated across every scrape that observed it and keyed by a Tango-minted opportunity_id.
Beta. Coverage is partial and grows by jurisdiction; field availability varies by state. Call /api/sled/opportunities/coverage/ before treating a per-state count as market size.
Note: The full field set is available via the shape query parameter, and show_shapes=true reports the tree your plan can reach. See Response Shaping.
This data does not join to the federal data¶
There is no UEI, no PIID, no agency-hierarchy key and no NAICS/PSC crosswalk. organization here is three strings — state, level, agency text — not the federal 7-key office payload, and state agencies have no entry in Tango's organization tree. incumbent_name on a forecast is published text, not a resolved Tango entity.
status is Tango's, and the portal's word is separate¶
status is Tango-owned liveness, derived from the portal's own word, the response deadline and the clock, and refreshed every fifteen minutes whether or not the feed moved. It is not the feed's status: portals drop a closed solicitation rather than restating it, so most of what the feeds still call open already has a deadline in the past. The feed's word is served separately as source_status, and its full raw vocabulary sits behind the plan-gated raw.
A list request with no status or active filter returns open solicitations only. A detail request returns the solicitation whatever its status.
Observation timestamps are not publication timestamps¶
Every timestamp whose name contains seen is ours, not the agency's. A state portal restates one page in place and publishes no amendment date, so last_change_seen_at is the scrape that caught the diff and its resolution is that state's crawl cadence. first_seen_at is when we first observed the record, which is the right field to poll on and the wrong field to read as a posting date — posted_date is that, where the portal publishes one.
Category scheme tagging is partial¶
category_codes is an array of {scheme, code} objects over nigp, unspsc, naics, text and unknown. Scheme tagging is mid-migration: roughly half of all entries corpus-wide are still bare pre-migration strings with no scheme, which read as unknown.
Consequence: ?naics= matches only the small share of entries tagged as NAICS. Use ?category_code= to match a code under any scheme including the untagged strings. See Category codes.
Solicitation fields (/api/sled/opportunities/)¶
| Field | Type | Description | Source |
|---|---|---|---|
opportunity_id | String (UUID) | Tango-minted stable primary key; the detail lookup value. | Tango |
solicitation_number | String | The number a human would quote. Null on roughly a third of the corpus, where the portal publishes none. | Portal |
solicitation_type | String | rfp, ifb, rfq, rfi, itb, sole_source, grant or other. Null means the portal states no type — a distinct answer from other. | Portal / Tango |
solicitation_type_source | String | Where the type came from: portal, title, description or none. About half of type fill is inferred rather than published. Detail default. | Tango |
title | String | Solicitation title. | Portal |
description | String | Free-text description. Detail default only — median around 550 characters, tail past 120,000. Null on about a quarter of open rows and on every row in several states. Reach it on a list with ?verbose=true or by naming it. | Portal |
state | String (2) | Two-letter state or territory code. The primary partition. | Portal |
jurisdiction | String | Level of government: state, local or education. Null on aggregator portals that cannot distinguish state from local (filter with jurisdiction=unknown). | Portal / Tango |
agency | String | The buying body as raw published text. No code resolution — some states emit bare agency numbers. | Portal |
status | String | Tango-derived liveness: open, closed, awarded, cancelled, unknown. | Tango |
status_reason | String | Which input decided status: deadline_future, deadline_past, no_deadline, source_terminal, delisted. | Tango |
delisted_at | Datetime | When a complete crawl of the portal first found the solicitation gone, stamped only if that was before its deadline. Null when never delisted or listed again since. Non-null means status=closed, status_reason=delisted unless the portal declared a terminal status. API 4.25.3+. | Tango |
status_computed_at | Datetime | When Tango last recomputed status. Detail default. | Tango |
source_status | String | The portal's own status word, frozen at last capture. Not liveness — see above. | Portal |
source_url | String | Link to the solicitation on its own portal. | Portal |
posted_date | Datetime | When the portal published it, where it publishes a date. Null on roughly a sixth of the corpus. | Portal |
response_deadline | Datetime | Current response deadline. Drives status. | Portal |
response_deadline_original | Datetime | The first deadline ever observed, which is what makes a moved deadline legible. Detail default. | Tango (provenance) |
bid_opening_date | Datetime | Parsed bid-opening date. Populated only where the portal encodes it machine-readably (a small minority of rows) — the human spellings are too ambiguous to parse safely. Detail default. | Portal |
bid_opening_raw | String | The portal's own bid-opening string, verbatim. Detail default. | Portal |
category_codes | Array of objects | {scheme, code} entries over nigp, unspsc, naics, text, unknown. Scheme tagging is partial — see above. | Portal / Tango |
has_documents | Boolean | Whether the solicitation advertises at least one document. Excludes auto-generated cover sheets. | Portal / Tango |
first_seen_at | Datetime | When Tango first observed the solicitation. The polling primitive. | Tango |
last_seen_at | Datetime | The most recent scrape that observed it at all. | Tango |
last_change_seen_at | Datetime | When Tango observed the newest substantive change. A scrape date, not an amendment date. | Tango |
modified | Datetime | When the Tango row last changed. | Tango |
snippet | String | The passage that matched, injected only under ?search= and only on rows that matched on their description. Absent on a title-or-agency match. | Tango |
organization | Object | The buyer — see below. | Portal / Tango |
contact | Object | Buyer contact — see below. Detail default. | Portal |
meta | Object | Denormalized counts — see below. | Tango |
attachments | Array of objects | Advertised documents — see below. Detail default. | Portal / Tango |
revisions | Array of objects | Observed amendment history — see below. Detail default. | Tango |
raw | Object | The platform's unparsed payload. Small plan or above, and explicitly unstable: its shape varies by portal platform and is not a contract at any plan. Signed URLs are stripped. | Portal |
platform, native_id and external_id are accepted as support filters for reproducing a specific record with us. They are deliberately not in any response shape, and their values are not a contract — an earlier draft rendered three competing spellings of one solicitation number on a single row.
organization object¶
| Field | Type | Description |
|---|---|---|
state | String (2) | Two-letter state or territory code. |
level | String | state, local or education; null where the portal cannot distinguish. |
agency | String | The buying body as raw published text. |
Three strings, not the federal office payload. There is no organization_id to join on.
contact object¶
| Field | Type | Description |
|---|---|---|
name | String | Buyer contact name, as published. |
email | String | Buyer contact email, as published. |
phone | String | Buyer contact phone, as published. |
meta object¶
| Field | Type | Description |
|---|---|---|
attachment_count | Integer | Documents the record holds, excluding auto-generated cover sheets. Can be lower than the length of attachments — see below. |
revision_count | Integer | Substantive revisions, excluding enrichment. |
last_revision_kind | String | Kind of the newest substantive revision. Never enrichment. |
last_change_source_declared | Boolean | Whether the portal's own amendment marker moved at the emission behind last_change_seen_at. false means Tango inferred the change by diffing consecutive scrapes, which is the common case. |
attachments array¶
| Field | Type | Description | Source |
|---|---|---|---|
name | String | File name as advertised. | Portal |
extension | String | File extension. | Portal |
mime_type | String | Guessed from the extension; the extraction pass sniffs the bytes regardless. | Tango |
checksum | String | Content checksum. Null while a download is in flight. | Tango |
size_bytes | Integer | The stored object's size. Null means no blob is stored — never 0, which would read as an empty file. | Tango |
char_count | Integer | Characters extracted. Only meaningful paired with size_bytes. | Tango |
word_count | Integer | Words extracted. | Tango |
pages | Integer | Page count, where the format has pages. | Tango |
extraction_status | String | completed, empty or failed. empty is extraction succeeding with zero characters — almost always a scan awaiting OCR, a different problem from one that threw. | Tango |
download_status | String | Whether the document was retrieved. | Tango |
is_contested | Boolean | The stored object's checksum disagrees with what this record advertised — another solicitation's bytes under the same key. Served as a name and an existence, never as content. | Tango |
is_generated_summary | Boolean | An auto-generated portal cover sheet rather than a document the agency wrote. Still listed and still searchable, but excluded from meta.attachment_count and has_documents. | Tango |
first_seen_at | Datetime | When Tango first observed this document on the record. | Tango |
extracted_text | String | The document's extracted body. Small plan or above, API 4.25.1+, and not included by attachments(*) — ask for it by name. The key is absent (never null) when it is not being served to you. Never returned for a contested document, at any plan. | Tango |
size_bytes and char_count only mean something together. Three megabytes that yielded no characters is a scanned image; three megabytes that yielded 40,000 characters is a specification. Neither number says that alone.
No url, and the body is paid. There is no download URL on this endpoint. The document body is served as extracted_text on the Small plan and above — opt-in only, so attachments(*) does not carry it — and is one of three plan-gated fields on this resource alongside raw and revisions.changes.
Searching that text is a different thing, and it is ungated. ?search= matches inside document text on every plan including Free, and returns the parent solicitation's id with no fragment of the document. So did a document match is free; what does it say is Small and above. Buying the body does not change search.
A document flagged is_contested never returns extracted_text, at any plan: the file we retrieved for it does not match what the solicitation listed, so we cannot vouch that its text belongs to this record.
Why meta.attachment_count can be lower than len(attachments). A generated cover sheet is a file, not a document the agency wrote, so it is excluded from the count while still being listed and flagged. The count answers "does this record hold its solicitation package"; the array answers "what files exist". A record whose only file is a generated sheet holds no package.
revisions array¶
| Field | Type | Description | Source |
|---|---|---|---|
observed_at | Datetime | The scrape that saw this change. Not when the agency made it. | Tango |
sequence | Integer | 1-based within the record; what to page the history by. | Tango |
kind | String | deadline_change, status_change, documents_added, documents_removed, documents_replaced, title_change, content_change — plus enrichment on the nested route only. | Tango |
changed_fields | Array of strings | Which fields moved. Available on every plan. | Tango |
changes | Object | The per-field before and after. Small plan or above; the key is absent below it. | Tango |
source_declared | Boolean | true only where a portal's own amendment marker changed at this emission — about 5% of revisions. Everything else is Tango inferring the change from the diff. | Portal / Tango |
The revisions expansion excludes enrichment rows; the nested revisions route serves them. An enrichment is Tango filling in coverage it did not have rather than the agency amending anything, which is also why it is excluded from revision_count.
History starts when Tango started reading that jurisdiction, not when the solicitation was posted. For recently added sources that is days, not months.
Forecast fields (/api/sled/forecasts/)¶
A forecast is a state's own advance notice of something it intends to buy. Maryland is the only state publishing one today. Forecasts carry no status, no deadline and no liveness filter — currency is your call from the advertisement quarter.
| Field | Type | Description | Source |
|---|---|---|---|
forecast_id | String (UUID) | Tango-minted stable primary key; the detail lookup value. | Tango |
state | String (2) | Two-letter state code. | Portal |
agency | String | The buying body as raw published text. | Portal |
title | String | Forecast title. | Portal |
description | String | Free-text description. | Portal |
estimated_advertisement_date | Datetime | The start of the published quarter, not a posting date. A large share of rows publish no quarter at all. | Tango (normalized) |
estimated_advertisement_raw | String | The portal's own words, verbatim — e.g. "Q3 (Jan.-March 2027)". | Portal |
procurement_category | String | The portal's own category word, passed through verbatim. | Portal |
procurement_method | String | The portal's own method word, passed through verbatim. | Portal |
contract_term | String | Expected term as published. | Portal |
contract_number | String | The number the state expects to award under, where it publishes one in advance. | Portal |
incumbent_name | String | Incumbent vendor name as published. Not resolved to a Tango entity. | Portal |
mbe_dbe_goal | String | Published MBE/DBE participation goal. | Portal |
delivery_location | String | Delivery or performance location as published. Detail default. | Portal |
source_url | String | Link to the forecast on its own portal. | Portal |
source_status | String | The portal's own status word, where it publishes one. Not liveness. | Portal |
has_documents | Boolean | Whether the forecast advertises at least one document. Detail default. | Portal / Tango |
first_seen_at | Datetime | When Tango first observed the forecast. | Tango |
last_seen_at | Datetime | The most recent scrape that observed it. | Tango |
modified | Datetime | When the Tango row last changed. | Tango |
organization | Object | state, level, agency — narrower than a solicitation's, since the forecast feed carries no sub-state jurisdiction. | Portal |
contact | Object | name, email, phone, as published. Detail default. | Portal |
estimated_value | Object | min, max, raw — see below. | Tango (parsed) |
raw | Object | The platform's unparsed payload. Small plan or above, and unstable. | Portal |
estimated_value object¶
| Field | Type | Description |
|---|---|---|
min | Number | Low end of the published award band. |
max | Number | High end. Null when the band names only one number — that is a floor, not an unbounded ceiling. |
raw | String | The portal's free-text award band, preserved verbatim. |
The band is free text from a state spreadsheet, parsed at serve time. raw is always there to check the parse against.
Data sources¶
Solicitations and forecasts are collected from individual state, local and education procurement portals — there is no aggregated upstream feed. The set of portals grows over time; /api/sled/opportunities/coverage/ reports which jurisdictions are represented, how many records each holds, and when a record in that jurisdiction last changed.
Update frequency: per-portal crawl cadence, which differs by jurisdiction. status is recomputed every fifteen minutes independently of the crawl, so a deadline passing is reflected promptly even when the portal has not been read since.