The SAM.gov Opportunities API, which the General Services Administration (GSA) documents as the Get Opportunities Public API, "only provides the latest active version of the opportunity" [1]. A dataset that keeps how contract notices change, and the files attached to them, therefore has to capture each version when it appears. This note reads GSA's documentation for what the API leaves out, ties each kind of change to the Federal Acquisition Regulation (FAR) rule behind it and sets out a record design that keeps every version.
One special notice shows the gap. It was first posted on 13 February 2026 and revised on 6 November 2026 under a new opportunity ID, with a new title and a replacement spreadsheet [5]. SAM.gov's full CSV file carried both versions as separate active rows under their own IDs, posted 13 February and 6 November, with no column linking them, and the February spreadsheet stayed on the notice's attachment list with a deletion date [4, 5]. Neither version has a solicitation number, so only the parent link on the notice page ties the two together [4, 5].
Where SAM.gov notices come from
The FAR defines the governmentwide point of entry (GPE) as the single public point for "Government business opportunities greater than $25,000", located at sam.gov [11]. A notice must be published at least 15 days before a solicitation is issued, and "The publication date is the date the notice appears on the GPE" [13]. Solicitations synopsized there are made available there too, "including specifications, technical data, and other pertinent information" [14].
Two texts describe these notices in November 2026: codified FAR subpart 5.2 [13] and the FAR Overhaul's Part 5 deviation text, updated on 16 March 2026, with 33 agencies listed under Agency Deviations (count derived) [16, 17]. How notices sit beside award and spending records is set out in FPDS, SAM.gov and USAspending: where contract data comes from.
SAM.gov notice types and their fields
The public API lists nine current notice type codes, from presolicitation and solicitation to award notice and special notice, and two retired ones (count derived) [1]. Labels drift. The posting interface renamed code i to "Consolidate/(Substantially) Bundle" in production on 3 March 2023, and the public API page still showed the old label on 8 November 2026 [1, 3]. A dataset stores the code beside the label.
Each record has two types: type, the "Opportunity current type", and baseType, the "Opportunity original type" [1]. GSA's first example response is an award notice whose base type is a combined synopsis/solicitation [1]. For that combined form, FAR 12.603 merges the synopsis and the solicitation "into a single document", so the description text is the solicitation and needs the same versioning as a file [18].
SAM.gov's full bulk file had 47 columns and about 220 MB on 7 November 2026 [4]. It holds the notice text in a Description column, and it has no column for attachment links or an earlier version's ID (derived) [4].
How a notice changes
GSA's posting interface, which needs a federal system account, shows how versions are built [3]. A revision creates a new version with its own opportunity ID, a version index and the first version's ID as its parent, and one version at a time is flagged latest [3]. Cancel, uncancel, archive and unarchive each return a new ID too [3].
The FAR explains why notices change. A solicitation is amended when the government changes its requirements or terms, and the amendment states its number, date, page count, the change and any new closing date [19]. A change beyond what offerors "reasonably could have anticipated" means cancelling the solicitation and issuing a new one [19], which breaks any chain built on one solicitation number (derived). Cancellation notices are optional [13], so a notice that stops changing says nothing about what came next (derived).
What the SAM.gov Opportunities API returns
GSA's overview says the API returns only the latest active version and points to SAM.gov's Data Services for all versions [1]. Active notices are updated daily and archived ones weekly [1]. Each search needs a posted-date range of one year or less, with up to 1,000 records a page and a default of 1 [1].
The description field is a link that needs the API key appended, and resourceLinks is an array of attachment download URLs [1]. The documented response has no parent or earlier-version field (derived) [1], so this API alone cannot rebuild a version chain.
The page also disagrees with itself. Its Getting Started section gives /opportunities/v2/search, Example 1 uses /prod/opportunities/v2/search and the OpenAPI text /v1/search [1]. The request table makes the dates required and limit and offset optional, while the OpenAPI text marks limit and offset required and the dates required only with limit [1]. Its change log ends on 11 June 2021 [1].
Data Services offers a full CSV collated nightly and yearly archive files, with no file in the listing described as a version history (derived) [21, 22]. On 10 November 2026 the archive files were dated 8 November, one week after their 1 November date [22, 34]. Some archive rows carry odd dates, such as a presolicitation posted on 20 December 2029 and archived on 5 June 2010 [22].
SAM.gov API keys and rate limits
Every request needs a key, and "Request per day are limited based on the federal or non-federal or general roles" [1]. The Opportunities page gives no figures. The sibling Contract Awards API documents its own, from 10 requests a day for a non-federal user with no role to 10,000 for a federal system account [2]. Those belong to that API, so a client paces itself from the limits its own key receives (derived).
Attachments and solicitation documents
Resources are files or external links, public or controlled, and controlled files need extra permissions to download [3]. Files must be under 250 MB, in types that run from PDF and Office formats, legacy .doc, .xls and .ppt included, to images, video and zip files [3]. A deleteAll flag removes a file from every version of a notice [3], so a dataset that wants the file later has to store it while it is listed (derived).
Solicitations in the uniform contract format run in sections A to M, and Section J lists "the title, date, and number of pages for each attached document, exhibit, and other attachment" [23, 24]. That list is a checklist for the files captured, and the section letters give each page of extracted text a place in its document (derived).
Getting text out of the files
PDF "specifies the fonts and positions of the individual characters rather than the semantic units of the text", so words and reading order have to be rebuilt [6]. In the Docling report, a parser reads text tokens with their coordinates and renders an image of each page, and optional optical character recognition (OCR) for scanned pages ran "upwards of 30 seconds per page" on CPU [7]. A 300-page scanned file then takes at least 2.5 hours (derived) [7]. The same converter reads legacy .doc, .xls, .ppt and .rtf files only through LibreOffice [26].
The accuracy of layout models trained on scientific articles "drops significantly" on more diverse layouts, DocLayNet's authors found [27], and OmniDocBench saw a clear drop on multi-column and complex layouts across all models [29]. OCR errors carry into later steps. On historical newspapers, person-name recognition fell from 0.87 to 0.63 average F-score between the top and bottom of four OCR quality bands [8]. Ways to test an extraction tool on your own files are in how to test document extraction accuracy before you buy.
Linking notices to awards
The solicitation number is the shared key. Award notices carry it with the award number, amount, date and the awardee's ueiSAM field [1], and award records carry it in the Contract Awards API, which filters on solicitationID and returns coreData.solicitationId, and as the Solicitation Identifier in USAspending [2, 9]. Coverage differs. FAR 5.301 requires award notices only for some awards over $25,000, and excludes orders under subparts 16.5 and 8.4 apart from listed cases [10]. Award records change through modifications and deletions [2], and totals across those changes are the subject of how to count federal contract dollars without double counting.
A record design that keeps every version
Each notice version needs two times, when the agency posted it and when the dataset captured it, as SQL:2011's bitemporal tables keep real-world and recorded time apart [31]. Key each version by its opportunity ID and link it by parent ID, or by solicitation number where no parent is shown, with a cancel-and-reissue recorded as a break [3, 19]. Store each file under a hash digest, which FIPS 180-4 describes as a way to detect whether a message has changed, so a re-posted file with new bytes shows as a new document [32]. Keep the deletion flag and date on the link between file and version [3, 5], and tie each page of text to the file and the method that produced it [33]. General provenance fields are set out in datasets that state their limits.
Rillor's public procurement records hold notices, awards, suppliers, line items and attached documents linked across U.S. federal sources, with per-page text extraction including OCR, and keep each change to a record as a new version.
Read the full report
The full report adds the notice type table, the FAR rules behind each change, the documented limits and conflicts in GSA's pages, the extraction research and a field list for each record, with the complete source list.

