Skip to article

Procurement documents · Coverage evaluation

Check tender document coverage before building around attachment links.

Inspect a dated audit of 17 URLs from 13 public award notices. Distinguish notice links, retrievable files, document versions and extracted text before choosing an API.

Published September 22, 202610 min readBy Daniel

A tender documents API needs to be evaluated at the file level, not just by whether a notice contains a URL. Notice text, attachment metadata, downloadable bytes, document versions and extracted text are separate deliverables. A product that summarizes specifications needs more evidence than a feed with a nonempty document_urls field.

We audited the listed URLs for 13 Contracts Finder award notices in a pinned public sample. The records contain 18 link references to 17 unique URLs, but only one URL follows the source's direct attachment route. During our September 22 retrieval run, none produced a complete PDF or ZIP response: 14 returned HTTP 403, two ended in redirect failures and one returned an HTML company page.

Those are observations from one execution environment, not proof that the documents are missing or globally inaccessible. The downloadable manifest preserves the evidence and makes that distinction explicit.

A field named “documents” can hide several different capabilities. Define which one your application actually needs before comparing suppliers.

| Deliverable | Evidence to request | | --- | --- | | Notice text | The supplied notice description and its source/version | | Document metadata or links | Document identity, role, name and listed URL where available | | Retrieved file | Complete bytes, retrieval time, response metadata and content hash | | Document versions | Earlier and later observations linked to the same source document | | Extracted text | Extraction status, source-file hash, method and OCR handling |

An HTML notice page can be useful source evidence without being a downloadable specification. A portal link may require further navigation or authorized access. A PDF download can succeed while its pages contain scanned images, leaving text extraction as a separate task.

For a search index, these distinctions determine what can be indexed. For a requirements-extraction workflow, they determine whether the required input exists at all. This article stops at link and retrieval evidence; it does not claim to extract requirements from documents.

Inspect the source and sample boundaries

The input is the pinned September 1, 2026 public procurement sample used in earlier guides. We selected all 13 rows whose source is contracts_finder, retaining their source notice IDs, release IDs, titles, publication timestamps and document_urls arrays.

The parent sample was already filtered to notices with a CPV code beginning with 72. All 13 selected Contracts Finder records have award stage. This is therefore a bounded engineering example, not a random sample of open tenders or a provider-wide document-coverage estimate.

The listed URLs break down as follows:

| URL shape | Unique URLs | What the shape tells us | | --- | ---: | --- | | Contracts Finder notice page | 13 | A notice reference, not a direct file route | | Contracts Finder attachment route | 1 | A candidate attachment endpoint | | External page or procurement portal | 3 | An external destination requiring further assessment |

One external company-information URL appears in two notice records, explaining the difference between 18 references and 17 unique URLs. We fetched each unique URL once and kept the notice-to-link relationship separately.

These classifications are declared URL-shape rules, not publisher-supplied document-role labels. The normalized field mixes different kinds of links. Twelve notices have no listed direct Contracts Finder attachment route in this projection; that is not proof that their documents do not exist on a portal or elsewhere.

Read the document availability manifest

The checker made unauthenticated GET requests on September 22, 2026, between 07:51:42 and 07:51:50 UTC. It used ordinary redirect handling, a 20-second timeout and an 8 MiB response-body limit. It requested the exact listed URLs without discovering additional links on returned pages.

| Observed outcome | Unique URLs | Interpretation | | --- | ---: | --- | | HTTP 403 | 14 | Access denied to this request; file availability unresolved | | Redirect failure ending in HTTP 302 | 2 | The client could not complete the redirect chain | | HTTP 200 HTML | 1 | A retrieved page, not a verified tender file | | Complete response with PDF or ZIP signature | 0 | No such file was obtained in this run |

The 14 denied URLs include all 13 Contracts Finder notice pages and the one attachment route. The two redirect failures belong to external procurement-portal entrance links. The successful HTML response is a company-information page.

Consider “Renewal of SAN Support Contract with HPE.” Its record lists both a notice URL and a direct attachment URL. The attachment request returned 403. The correct manifest state is “access denied during this observation,” not “no attachment” or “document downloaded.”

The “CaseFlow” record lists a notice page, a procurement-portal entrance and a company-information page. Counting all three as files would inflate coverage before any bytes were inspected. In this run, the company page supplied the sole HTTP 200 response, which still did not establish attachment availability.

No 404 response was observed. The audit cannot identify the cause of the denials or establish whether an authorized integration or human browser could obtain the underlying documents. It also cannot assess file readability, extraction quality or historical versions because no file was successfully obtained.

Reproduce the report or make a new observation

The download includes two workflows. The default verifies the pinned sample and observation hashes and regenerates the report offline:

python3 -B check_links.py --output reproduced
python3 -B -m unittest test_check_links.py

It produces the link manifest, retrieval manifest, recorded observations and summary report. All four outputs were reproduced from the packaged evidence during preparation. Nine checks cover URL classification, HTML responses, file signatures, partial downloads, empty responses and HTTP failures.

To make a new live observation:

python3 -B check_links.py --live --output new-observation

That run has new timestamps and may have different results. Keep it alongside the earlier observation rather than overwriting history. A new outcome is evidence of changed retrieval behavior, not a failure to reproduce the historical report.

The classifier checks response bytes as well as content type. A successful response claiming application/pdf without a PDF signature is not counted as a PDF. A signature itself is only a file-format candidate, not a successful parse or evidence that the file contains the requested specification. Other formats remain separately classified. Over-limit bodies do not receive a full-body hash.

The bundle redistributes metadata and hashes, not response bodies. Production collection should separately agree permitted access, storage and reuse for each source.

Preserve document identity and retrieval versions

A useful document manifest needs more than a URL. Retain the source, parent notice/release identity, publisher document ID where available, listed URL, resolved URL, retrieval timestamp, response status and complete-file hash.

OCDS distinguishes identifiers for objects within their relevant context; avoid treating a document's local ID as a globally unique identifier. Carry the parent context when joining releases and documents. OCDS identifier guidance.

The flattened sample used here does not include publisher document IDs or document-version history. It supports a notice-version-to-URL relationship and one retrieval observation per URL. It does not support a claim that an attachment has remained unchanged since publication.

On subsequent retrievals, a changed complete-file hash identifies changed bytes. Investigate whether that represents a revised specification, a generated page, a different file or another change before assigning business meaning. ETag and Last-Modified can assist retrieval, but should not replace retained content evidence.

Check the specific API and access plan

Vendor documentation can help identify the right questions, but documented fields are not measured coverage of your intended source population.

Tender Impulse documents filepath and filename fields and a client download workflow. Its contract-award documentation also explicitly allows records with no attached document. Access is a paid service. Those statements describe its interface; we did not run an authenticated Tender Impulse retrieval for this audit. Tender Impulse API documentation.

Tango distinguishes attachment metadata from extracted document text. Its September 20 changelog states that attachments(*) no longer includes extracted_text; the text field must be requested explicitly and is subject to access rules. That is a concrete example of why wildcard field selection and plan access need to be verified. No authenticated Tango comparison was performed here. Tango changelog.

When evaluating a tender documents API, ask for a sample using your required source, notice stage and document role. Test the actual file route and, if needed, the separate text-delivery field. Do not infer all-source attachment coverage from a demonstration notice or assume a free metadata feed includes file bodies.

For notice collection itself, see the TED Search API guide and Find a Tender OCDS API guide. Their notice interfaces are only the starting point for a file-level coverage requirement.

Turn the findings into a document-coverage requirement

The included specification separates five deliverables: notice content, link metadata, original files, document-version observations and extracted text. Use it to define which are required for your product.

Choose a meaningful denominator. “Successful responses divided by URLs” is not the same as “eligible notices with all required specifications.” Several URLs can point to the same file, and a notice may require multiple document roles. Record unlisted, denied, redirected, confirmed absent and successfully retrieved cases separately.

Agree the source/stage/date scope, required formats, maximum sizes, access requirements, retry cadence and version-retention policy. If text is required, add extraction and OCR acceptance criteria against the retrieved file hashes. Set numerical coverage thresholds after a representative sample, not from these 13 award notices.

This audit gives no basis for promising attachment delivery for the selected source. It does provide a concrete list of access and metadata questions to resolve before agreeing that delivery.

Request a source-specific document sample

Start with the public manifest, then specify the source portals, notice stages and document roles your application needs. A useful evaluation request asks whether the service can supply identifiable, retrievable and versioned files for that scope, with failures reported explicitly.

Frequently asked questions

Does a document URL mean the API supplies the file?

No. It can identify a notice, portal or direct attachment route. Verify the response and file bytes, and agree whether the supplier delivers stored files or only source links.

Do the 403 responses prove the documents are missing?

No. They establish access denial for this retrieval run. The audit does not determine the cause or whether another authorized access path can retrieve the documents.

Did this example verify text extraction or historical attachment versions?

No. It records one set of link-retrieval observations and obtained no complete PDF or ZIP response. Text extraction, document relevance and historical version coverage remain unverified.