A copy of API v3.0 - Documentation v2.1 - Summary Records on docs.openopps.com, last changed there on 30 October 2026. Plans and prices are on the developer portal.
Contents
- Changes since v2.0
- Guidance for developers, including LLM developers
- JSON Output Structure and Information Design Principles
- Example document
- Field list
- API requests
- Further code examples
- Example Javascript code
- Example Python code
- Document Attachments
- Filtering For Attachments
- Show Attachment Sources
- Downloading Attachments via Spend Network API
- How It Fits Together
- Best Practices
Documentation API - Summary Records Endpoint (Version 3)
Created: 6 October 2026
Replaces: Documentation v2.0 (created 16 February 2026)
https://openopps.com/pricing/#api-pricing/
Downloadable version.
api-v30-documentation-v21-summary-records.md 58334
Changes since v2.0
Version 2.1 clarifies how fields should be interpreted (dates and time zones, status, values, identifiers, duplicates and CPV scores), documents search and filtering behaviour, corrects field descriptions and limits, and removes references that are no longer accurate.
Spend Network's summary endpoint provides a simplified JSON output that provides a flatter, smaller, more consistent output. This enables lower volumes of data to travel into databases and more consistent and easier parsing of the data.
Guidance for developers, including LLM developers
Advice for teams working with our API. For teams using LLMs working with LLMs is fine, we do it and we're happy for our clients to do the same. Make sure you give the LLMs this information.
- Be mindful of our publishing schedule - We publish new notices twice a day only. Typically at 6am and 11am GMT - this timing can vary depending on the volume of publishing. Don't all hit the API at precisely 6AM, you'll get queued. Use your brains, or whatever cognitive capacity you have to make a good decision.
- Caching data - If you are caching data DO NOT set your script to poll the API on schedules between these times (e.g. every few minutes, you'll waste our resources and get NO NEW data).
- Be respectful - New notices are published twice a day, so polling more often than that returns no new data. Collect once after each publishing run. Excessive use may get your account blocked temporarily or permanently, and we will not reimburse you if you lose access as a result. If you need more capacity, speak to us.
- Download this documentation file and add it to your project - Make sure your LLM knows to reference the documents and that it is in the chat context when writing code to access the API and to parse results of notices.
- Assume that completion is ragged - Every field other than the title, the augmented CPV codes and the published date are optional. Some have an award value, some have a range, some have nothing. Code like
data.results[0].supplier.namewith no guard throws on the first record from a sparse source, or silently produces wrong output. We try to populate as much as we can but the underlying data sometimes just doesn't exist. - Do not index into a nested field without a presence check, and do not assume a field that appeared in one record exists in the next. Write defensive accessors. Check for presence at every level of nesting. Treat a field that was populated in record 1 as entirely capable of being absent in record 2.
- Do not guess field names - the field listing is here for a reason. Use it. Pattern-matching to what a procurement API "should" return is the most common single failure. Names like
contract_value,deadline,cpv, andtender_idget written from prior expectation rather than from the actual schema. When the real names differ, the code either throws or returnsundefinedeverywhere. Use the documented schema and the worked example responses. Do not infer field names from the domain. - Do not invent the status or category vocabulary - Release tags come from the published list in this document, not from a guess. Do not use
tag_statusto decide whether a notice is currently open: usetender_end_date. - Understand our maximums - You can receive no more than 10,000 documents in a single request. You can use
limitvalues to cap the number of documents you will receive. - Pagination: get the terminating condition right - Do not grab the first page and stop. The API uses offset-based paging: increase
offsetbylimiton each call and stop when a page returns fewer records thanlimit. A single query returns no more than 10,000 records. - Augmented CPVs - we provide five augmented CPVs for every document, each with a relevance score. Augmented codes are assigned at the four-digit group level (e.g.
34120000). CPV filtering matches publisher codes and augmented codes, so filtering on a group-level code reaches notices whose publisher gave no CPV, regardless of language or country. A filter on a more specific code (e.g.34121000) matches publisher codes only. Because all five augmented codes are matched, usecpv_aug_threshold(0 to 100, the same scale asrelevance_score) to exclude weak matches. - Search is relevance-ranked, not boolean keyword matching - Results are returned by relevance. A top result may not literally contain the query term. Do not "fix" this by post-filtering on string match, because that throws away the best results. Treat absence of a literal keyword match as expected behaviour, not a bug. Do not add string-match post-filtering on top of ranked results. When
search_term__isis set, results are ordered by relevance andsort_bydoes not apply. Inloosemode, use a wildcard to catch inflected forms:autobus*matches autobusy and autobusów,autobusdoes not. - Deduplicate across sources - Because the data is aggregated, the same underlying tender can appear from multiple portals, each with its own
ocid. Setdedupe__istotrueto remove the duplicates we have identified, using fuzzy matching and deterministic drift. Because we are cautious, you may still see what looks like a duplicate. Usually this is because a notice contains sufficient deviation in key areas (e.g. deadline date) between notices to warrant publishing both versions.result_countis counted before deduplication, so it can exceed the number of records you receive. - Handle rate limits and read the error body - Implement backoff rather than retrying immediately or not at all. Read the error response body, not just the HTTP status code, because useful detail is carried in the payload.
- Expect non-ASCII text - With 160+ countries in scope, expect diacritics and non-Latin scripts in fields like buyer and supplier names. Handle Unicode correctly end to end so names are not mangled.
JSON Output Structure and Information Design Principles
The JSON output for the API has been meticulously structured to ensure clarity, consistency, and ease of use. The following key principles have been adopted in its design:
1. A single document structure for all records
- Consistency: All records, whether tender notice, planning or contract awards contain the same fields and all fields deploy consistent data structures at all times. This ensures that simple validation routines can be written for each document (e.g. a checksum for the total number of fields).
- Use: Tender documents will contain fields for awards data, which will not be populated, it is critical for developers to use
release_tagsto determine the nature of the published document before accessing the appropriate data.
2. Clear and Descriptive Naming Conventions
- Consistency: Each key follows a consistent naming pattern that aids in easy identification and understanding. For example, all monetary values related to awards are prefixed with
award_, making it straightforward to locate and differentiate from other values liketender_. - Descriptiveness: Keys are named descriptively to convey the precise information they hold. For instance,
buyer_address_country_nameclearly indicates it stores the country name of the buyer's address.
3. Logical Grouping of Related Information
- Categorisation: Related data points are grouped together logically. For example, all keys related to buyer information start with
buyer_(e.g.,buyer_name,buyer_address_country_name), ensuring that similar information can be found in proximity. - Hierarchical Structure: Our data is broadly flat, but the small amount of hierarchy helps users navigate the data efficiently, recognizing patterns and relationships between different pieces of information.
4. Effective handling of one to many relationships
- Managing edge cases: In rare cases contract award notices are published as an extension to a tender notice, in still rarer circumstances there are more than one award published against a given tender. We handle this dynamically, collating data where appropriate (e.g in the
award_titles) and using the first award found in fields where referential integrity is essential. This is the case in fields with the_firstsuffix. - Implications: In some cases it is possible that only the first award from a set of multiple awards is being documented. In this context developers can use our complete API to gather additional records from the original published releases where necessary.
5. Comprehensive Data Coverage
- Detailed Information: The JSON structure includes extensive details covering various aspects such as buyer details, award values in multiple currencies, tender specifics, and more. This comprehensive coverage ensures users have all necessary data in one place.
- Multiple Data Types: The structure accommodates various data types including strings, numbers, arrays, and booleans, to accurately represent different kinds of information.
6. Timestamp and Date Management
- ISO 8601 Format: All date and time values are provided in ISO 8601 format (e.g.,
2021-12-17T08:27:49Z), a widely accepted standard that supports interoperability and ease of parsing across different systems. - Time zones:
date_createdanddate_updatedare recorded in UTC, although they carry no offset in the output. Some source dates, includingrelease_dateandtender_end_date, are published without a time zone offset; do not assume these are UTC. - Clear Distinction Between Dates: Different types of dates such as
release_date,start_date, andclosing_dateare clearly differentiated, ensuring precise tracking of timelines.
7. Nullability for Optional Fields
- Explicit Nulls: Fields that might not always have a value (e.g.,
contact_url,buyer_address_street_address) are included with a null value when applicable, maintaining a consistent structure while signalling optional data.
8. Array Utilization for Multiple Entries
- Lists for Multiple Items: Fields that can have multiple entries, such as
supplier_names,cpv_names, andsupplier_ids, are represented as arrays. This approach ensures flexibility and scalability in handling multiple values.
9. Identification and Referencing
- Unique Identifiers: Keys like
ocidandtender_idare used to provide unique identifiers for contracts and tenders, supporting traceability and integration with other datasets. Where we are able to we will supply identifiers for suppliers with a unique id for companies to reconcile to the source. - References:
references_ocidslists other records that our matching process found similar to this one, each with a similarity score between 0 and 1. A listed record may be a different procurement by the same buyer, so treat it as a hint to investigate, not as a confirmed link.
10. Currency and Value Clarity
- Explicit Currency Indicators: Monetary values are accompanied by currency indicators (e.g.,
award_currency_first,tender_currency), ensuring that users can accurately interpret financial data. - Multiple Currency Representations: Values are provided in different currencies (USD, GBP, EUR) to cater to a diverse range of users and use cases.
11. Status and Metadata Information
- Status Indicators:
tag_statusgives the status provided by the publisher at publication, anddps_predindicates whether we have identified the contract as a framework or dynamic purchasing system. - Metadata for Traceability: Metadata fields such as
date_createdanddate_updatedprovide insights into the data's lifecycle, aiding in version control and auditing.
This structured approach ensures that the JSON output is both user-friendly and comprehensive, facilitating efficient data retrieval and analysis for diverse applications.
Example document
This example document shows how summary records data is structured and published.
{
"has_attachments": true,
"cpv_names": [
"Education and training services",
"Personnel services except placement and supply services",
"Specialist training services",
"Vocational training services",
"Personal development training services",
"Business and management consultancy services",
"Personnel services except placement and supply services",
"Specialist training services",
"Vocational training services",
"Personal development training services",
"Business and management consultancy services",
"Education and training services"
],
"tender_url": "https://www.contractsfinder.service.gov.uk/Notice/12ef087c-3ad3-4d72-b2bb-e3d416081402",
"closing_date": null,
"awards_usd_value": 0,
"date_updated": "2024-01-18T02:40:12.144016",
"buyer_address_country_name": "United Kingdom",
"tender_eur_value": 0,
"cpv_aug_names": [
"Personnel services except placement and supply services",
"Specialist training services",
"Vocational training services",
"Personal development training services",
"Business and management consultancy services"
],
"cpv_aug_codes": [
"79630000",
"80510000",
"80530000",
"80570000",
"79410000"
],
"buyer_name": "DRIVER AND VEHICLE STANDARDS AGENCY",
"tender_id": "tender_362771/1301350",
"language": "en",
"source": "cf_notices",
"release_tags": "planning",
"cpv_codes": [
"80000000",
"79630000",
"80510000",
"80530000",
"80570000",
"79410000",
"79630000",
"80510000",
"80530000",
"80570000",
"79410000",
"80000000"
],
"contracts_eur_value": 0,
"buyer_address_street_address": "112 Upper Parliament Street",
"buyer_address_locality": "Nottingham",
"buyer_address_iso_alpha2": "GB",
"contracts_gbp_value": 0,
"contact": "epscategoryteam@dvsa.gov.uk",
"awards_gbp_value": 0,
"supplier_names": [],
"processing_status": "success",
"supplier_ids": [],
"ocid": "ocds-0c46vo-0001-12ef087c-3ad3-4d72-b2bb-e3d416081402",
"buyer_address_region": "England",
"contracts_usd_value": 0,
"start_date": null,
"dps_pred": false,
"contact_url": null,
"date_created": "2024-01-16T02:35:38.235781",
"awards_eur_value": 0,
"tender_description": """Driver & Vehicle Standards Agency""",
"award_descriptions": "",
"tender_gbp_value": 0,
"tender_usd_value": 0,
"cpv_aug_data": [
{
"cpv_aug_names": "Personnel services except placement and supply services",
"cpv_aug_codes": "79630000",
"relevance_score": 16.9238
},
{
"cpv_aug_names": "Specialist training services",
"cpv_aug_codes": "80510000",
"relevance_score": 15.2334
},
{
"cpv_aug_names": "Vocational training services",
"cpv_aug_codes": "80530000",
"relevance_score": 9.0345
},
{
"cpv_aug_names": "Personal development training services",
"cpv_aug_codes": "80570000",
"relevance_score": 8.3424
},
{
"cpv_aug_names": "Business and management consultancy services",
"cpv_aug_codes": "79410000",
"relevance_score": 7.7439
}
],
"release_date": "2024-01-15T12:32:28Z",
"references_ocids": [],
"tag_status": "open",
"tender_title": "Tailored Executive Development",
"award_titles": ""
}
The following shows an example of a document where lotting features have been identified and collected.
{
"ocid": "ocds-0c46vo-0001-036588-2025_036588-2025",
"start_date": "2025-09-04T00:00:00+01:00",
"date_created": "2025-07-03T01:37:44.838811",
"tender_description": "Nest is on a path to becoming one of the largest pensions schemes in the UK and our growth trajectory means that the operating model for Nest Invest will need to evolve over the next ten years.\nIn order to support the continued strategic growth and development of the investment function Nest Invest will require ongoing specialist consultancy support for the development of the organisational model and development of the investment approach",
"award_descriptions": "",
"tender_gbp_value": 10000000,
"lots": {
"lot_descriptions": [
"1: Strategic investment business, organisational strategy and governance advice with extensive proven experience of international investment best practice with large Financial Conduct Authority (FCA) regulated Occupational Pension Schemes (OPS) with assets under management of £25 billion and above",
"2: Target Operating Model design and delivery including the transition and implementation of operating models for fund administration and custody service including additional SME operational support as needed to large Financial Conduct Authority (FCA) regulated Occupational Pension Schemes (OPS) (or similar) with assets under management of £25 billion and above. 35",
"3: Specialist investment consultancy services to large Financial Conduct Authority (FCA) regulated occupational pension schemes (OPS) with assets under management of £25 billion and above"
],
"lot_classifications": [],
"lot_earliest_start_date": "2025-09-04T00:00:00+01:00",
"lot_statuses": [
"active"
],
"lot_identifiers": [],
"lot_ids": [
"1",
"2",
"3"
],
"lot_currencies": [
"GBP"
],
"lot_total_value": 10000000.0,
"lot_count": 3,
"lot_amounts": [
3500000.0,
3500000.0,
3000000.0
],
"lot_titles": [
"1: LOT 1 Strategic investment business, organisational strategy and governance advice",
"2: LOT 2 Target Operating Model design and delivery including the transition and implementation of operating models for fund administration and custody service including additional SME operational support as needed",
"3: Specialist investment consultancy services to large Financial Conduct Authority (FCA) regulated occupational pension schemes (OPS"
],
"lot_latest_end_date": "2029-09-03T23:59:59+01:00",
"lot_primary_currency": "GBP",
"lot_procurement_categories": []
},
Field list
Below we have grouped fields and listed each possible field then and provided a field type and description.
Currencies and conversion
Our currency output adheres to international currency standards by using ISO 4217 codes for currency representation. This ensures that monetary values are clearly identified and standardised across different datasets, facilitating accurate interpretation and conversion. By employing ISO currency codes such as GBP (British Pound), USD (US Dollar), and EUR (Euro), the data maintains consistency and interoperability in financial reporting and analysis.
We use https://exchangeratesapi.io/ for currency conversion, which provides several key benefits, including accurate and up-to-date exchange rates sourced from reliable financial data providers, enhancing the reliability of financial reporting, supporting a wide range of currencies, enabling comprehensive and global financial analysis. Integrating this API into our system ensures that all currency-related data is consistent, precise, and reflects the correct conversion rates at the time of publication.
Values are provided as published by the source. We do not convert between net and gross amounts.
| Code | Field type | Description |
|---|---|---|
award_currency_first |
String | The original currency of an award notice as published in the source data, where the currency is adequately labelled and can be determined by Spend Network. This currency may be any known currency. |
award_amount_sum |
Value | The original value of an award as published in the source data, where the value is adequately structured and can be converted to a value field by Spend Network. |
awards_usd_value |
Value | The value of award_amount_sum converted from the award_currency_first to USD where the original source is not in USD. Conversion completed on the date of collection, we use https://exchangeratesapi.io/ as our source for currency conversion data. |
awards_gbp_value |
Value | The value of award_amount_sum converted from the award_currency_first to GBP where the original source is not in GBP. Conversion completed on the date of collection, we use https://exchangeratesapi.io/ as our source for currency conversion data. |
awards_eur_value |
Value | The value of award_amount_sum converted from the award_currency_first to EUR where the original source is not in EUR. Conversion completed on the date of collection, we use https://exchangeratesapi.io/ as our source for currency conversion data. |
tender_currency |
String | The original currency of a tender notice as published in the source data, where the currency is adequately labelled and can be determined by Spend Network. This currency may be any known currency. This value is only populated when the source is in OCDS format and the contract notice is appended to a tender notice in a manner compliant with the standard. |
tender_amount |
Value | The original value of a tender as published in the source data and where the value is adequately structured and can be converted to a value field by Spend Network. This value is only populated when the source is in OCDS format and the contract notice is appended to a tender notice in a manner compliant with the standard. |
tender_usd_value |
Value | The value of tender_amount converted from the tender_currency to USD where the original source is not in USD. Conversion completed on the date of collection, we use https://exchangeratesapi.io/ as our source for currency conversion data. This value is only populated when the source is in OCDS format and the contract notice is appended to a tender notice in a manner compliant with the standard. On award notices, use award_amount_sum and awards_*_value for award values. |
tender_gbp_value |
Value | The value of tender_amount converted from the tender_currency to GBP where the original source is not in GBP. Conversion completed on the date of collection, we use https://exchangeratesapi.io/ as our source for currency conversion data. This value is only populated when the source is in OCDS format and the contract notice is appended to a tender notice in a manner compliant with the standard. On award notices, use award_amount_sum and awards_*_value for award values. |
tender_eur_value |
Value | The value of tender_amount converted from the tender_currency to EUR where the original source is not in EUR. Conversion completed on the date of collection, we use https://exchangeratesapi.io/ as our source for currency conversion data. This value is only populated when the source is in OCDS format and the contract notice is appended to a tender notice in a manner compliant with the standard. On award notices, use award_amount_sum and awards_*_value for award values. |
Entities and Organisations
During our input process we are cleansing fields to provide, reliable consistent values across both buyer names and addresses. In some cases we also match supplier identifiers. The cleansing of buyer names is primarily for the UK, with new links being developed all of the time across other countries. Addresses are cleansed through a proprietary address management algorithm, splitting up codes, identifying postcodes and allocating clean country names, before then deriving a country code.
| Name | Field type | Description |
|---|---|---|
buyer_name |
String | This field contains the name of the buying entity or organisation. It identifies the buyer involved in the tender or contract, facilitating recognition and tracking of the entity. |
buyer_address_country_name |
String | The name of the country where the buyer's address is located. Where the source provides an identifiable country name we will amend this entry to provide a consistent reference, e.g. "UK" is converted to "United Kingdom" |
buyer_address_locality |
String | The locality or city where the buyer's address is situated. Where we can we seek to extract and separate address features into the correct patterns for greater geographical analysis. |
buyer_address_region |
String | The region where the buyer's address is situated. Where we can we seek to extract and separate address features into the correct patterns for greater geographical analysis. |
supplier_names |
String | This field lists the names of the suppliers involved in the contract or tender, the data is provided in a list format. |
supplier_ids |
List of strings | This field contains unique identifiers for the suppliers, such as registration or company numbers. Where we have reconciled an identifier to a register, it carries a prefix naming that register; identifiers published by the buyer are given as published. Items in this list correlate to the provided supplier name, null items are recorded so that the third id in the list will correspond to the third supplier name in the supplier_name list. |
buyer_address_street_address |
String | This field represents the street address of the buyer. Where we can we seek to extract and separate address features into the correct patterns for greater geographical analysis. |
buyer_address_iso_alpha2 |
String | This field uses the ISO 3166-1 alpha-2 code to represent the buyer's country. It provides a standardized and internationally recognized format for country identification. |
Dates and Times
The following dates and times are used in the contract award documents.
| Field Name | Field type | Description |
|---|---|---|
award_start_date_first |
Date time | The contract start date. It indicates when the contract is scheduled to commence, _first denotes that this is the start date for the first published award. |
award_end_date_first |
Date time | The contract end date. It indicates when the contract is scheduled to be completed, _first denotes that this is the end date for the first published award. |
release_date |
Date time | The publisher's own record for the date that the document was published. Some sources publish this without a time zone offset; do not assume it is UTC. |
date_updated |
Date time | The most recent modification of the record by Open Opportunities, in UTC. |
tender_end_date |
Date time | The submission deadline published by the source. Some sources publish this without a time zone offset; do not assume it is UTC. Use this field to decide whether a tender is currently open. |
date_created |
Date time | This field indicates the date gathered by Spend Network. It marks when the data was collected and recorded, in UTC. If there is no release date or start date, this date is the best substitute. |
Document identifiers
We use the following document identifiers in the output, these are identifiers for the documents, OCIDs can be used as reliable database keys and are unique. By using these identifiers, we ensure that each document can be accurately tracked, referenced, and integrated into various data systems, supporting robust data management and analysis practices.
| Field Name | field type | Description |
|---|---|---|
tender_id |
String | The identifier for the notice, taken from the source. For some sources we derive it from the source's notice number or URL, so it may not match the reference shown on the publisher's site. |
ocid |
String | This field is Spend Network's unique identifier, compliant with the Open Contracting Data Standard. It ensures consistent and standardised identification of contracts. |
references_ocids |
Array of objects | Other records that our matching process found similar to this one, each given as {ocid: similarity score} with a score between 0 and 1. A listed record may be a different procurement by the same buyer. Treat these as hints to investigate, not as confirmed duplicates or procurement links. |
Status and Metadata
Status and metadata tags provides information on the origin of the data, categorization of document types, the status of contracts, and classification of contract types. Each entry ensures clarity and consistency, facilitating accurate data interpretation and analysis.
| Field Name | Data Type | Description |
|---|---|---|
source |
String | This field contains the name of the source code used to harvest the data. It indicates the origin of the data collection process. |
release_tags |
String | The document type. See Release Tags below for the full list of values. A small number of sources publish amendments as stand-alone documents; these arrive as separate records with an amendment release tag and are not linked to the original notice. |
tag_status |
String | The status provided by the publisher when the notice was published. It is not updated as the notice progresses and must not be used to show whether a notice is currently open or closed. Use tender_end_date for that. |
dps_pred |
Boolean | This field denotes whether Spend Network has identified that this contract is a framework or dynamic purchasing system (DPS) contract. It helps in classifying the type of contract. |
language |
String | ISO 639 language classification, automatically derived from a Google large language model. |
Descriptions and Titles
The following table provides a detailed description of the narrative fields within the JSON output related to tenders and awards.
→ award titles → comes in as a list, converted to a string (never an array)
→ Only for OCDS linked titles and awards
| Field Name | Data Type | Description |
|---|---|---|
tender_title |
String | This field contains the title of the tender. It provides a brief and specific name for the tender, summarising its main purpose. This value is only populated when the source is in OCDS format and the contract notice is appended to a tender notice in a manner compliant with the standard. |
content |
String | This field combines both the tender title and description, enabling more efficient in-memory text searching. |
tender_description |
String | This field provides a detailed description of the tender. It includes the published information about the tender's requirements and objectives. This value is only populated when the source is in OCDS format and the contract notice is appended to a tender notice in a manner compliant with the standard. |
award_descriptions |
String | This field contains descriptions of the awards associated with the tender. It provides published information about the awards granted. In the event that multiple awards are added as separate releases to a single tender, the awards descriptions will be concatenated in this field |
award_titles |
String | This field includes the titles of the awards. It gives a concise and specific name for each award associated with the tender. In the event that multiple awards are added as separate releases to a single tender, the awards titles will be concatenated in this field. |
Contact Information
The following table provides a detailed description of specific fields within the JSON output related to contact information. Each field is accompanied by its data type and a brief description, facilitating a better understanding of the contact details provided and aiding in efficient data interpretation and analysis.
| Field Name | Data Type | Description |
|---|---|---|
contact |
String | This field contains the email address of the contact person or entity. It provides a direct communication channel for inquiries or further information. |
contact_url |
String | This field includes the URL for contact information. It offers an online link for additional contact details or communication methods. |
Classification Codes
The following table provides a detailed description of specific fields within the JSON output related to procurement classification. Each field is accompanied by its data type and a brief description, facilitating a better understanding of the procurement categories and aiding in efficient data interpretation and analysis. CPV stands for Common Procurement Vocabulary, the EU procurement classification system, while "aug" stands for augmented, indicating additional codes added by Spend Network.
| Field Name | Data Type | Description |
|---|---|---|
cpv_names |
Array | This field contains an array of names from the Common Procurement Vocabulary (CPV) that were provided by the original publisher. |
cpv_aug_names |
Array | This field includes an array of augmented CPV names added by Spend Network. It offers additional classification names that enhance the standard CPV categorisation. |
cpv_aug_codes |
Array | This field contains an array of augmented CPV codes added by Spend Network. It provides additional classification codes that complement the standard CPV codes. |
cpv_codes |
Array | This field includes an array of CPV codes from the Common Procurement Vocabulary that were provided by the original publisher. |
cpv_aug_data |
Array of objects | One entry per augmented code, giving cpv_aug_codes, cpv_aug_names and relevance_score (0 to 100). |
Augmented codes are assigned at the four-digit group level (e.g. 34120000).
URLs
The following table provides a detailed description of a specific field within the JSON output related to the URL of the published tender.
| Field Name | Data Type | Description |
|---|---|---|
tender_url |
String | This field contains the original source URL of the published tender. It provides a direct link to the tender's publication, enabling access to the full tender details and documentation. |
Lots
The following table provides a detailed description of a specific field within the JSON output related to lots in a published document.
| Field name | Data Type | Description |
|---|---|---|
lots.lot_count |
Integer | The number of lots into which the tender or award is divided. Used to identify whether the procurement is split and how many components it includes. |
lots.lot_ids |
Array of Strings | A list of identifiers for each individual lot. These identifiers are typically numeric strings (e.g., "1", "2"). |
lots.lot_titles |
Array of Strings | Titles of each lot. These provide a concise description of the lot's scope or purpose. Titles may include a prefix indicating lot number. |
lots.lot_descriptions |
Array of Strings | Descriptive narrative for each lot. These typically include eligibility criteria, scope, or technical requirements. |
lots.lot_amounts |
Array of Numbers | Monetary values for each lot, expressed in the primary currency. These represent the estimated or awarded value for each lot. |
lots.lot_total_value |
Number | The total value of all lots combined. Used for summarizing the overall contract value when split across multiple components. |
lots.lot_primary_currency |
String | The ISO 4217 currency code used for all monetary amounts within the lot object (e.g., GBP). |
lots.lot_currencies |
Array of Strings | List of currencies applicable to the lots. Useful in cross-border or multi-currency procurement. |
lots.lot_earliest_start_date |
DateTime | The earliest scheduled start date for any of the lots. |
lots.lot_latest_end_date |
DateTime | The latest scheduled end date for any of the lots. |
lots.lot_statuses |
Array of Strings | Statuses of individual lots (e.g., "active", "cancelled"). Multiple statuses may exist across lots in the same contract. |
lots.lot_classifications |
Array | A placeholder for CPV or other classification codes relevant to the lots. Currently not populated. |
lots.lot_procurement_categories |
Array | Reserved for categorisation of lots by procurement category (e.g., goods, services, works). Currently not populated. |
Attachments
The following table provides a detailed description of a specific field within the JSON output related to the availability of attachments for a given document.
| Field Name | Data Type | Description |
|---|---|---|
has_attachments |
Boolean | A field that shows whether a document has attachments associated with it. |
attachments |
Array | This field contains a list of urls for attachments that have been found for the given search. Each entry has a field for title and url. This field is only present when the user has added attachment__show to their API request. |
attachments.title |
String | The title of an attachment gathered from a specific page. This field is only present when the user has added attachment__show to their API request. |
attachments.url |
URL | The original URL of the attachment. This field is only present when the user has added attachment__show to their API request. |
API requests
Accessing the Spend Network API Endpoint
The summary records endpoint uses a POST request with a JSON body.
API Endpoint URL
POST https://api.spendnetwork.cloud/api/v3/notices_summary/read_summary_records
Request Body Parameters
All parameters are optional and passed in the JSON request body:
| Name | Type | Description |
|---|---|---|
search_term__is |
string | Search term to include (maxLength: 2000). Supports multi-word phrases, boolean operators (AND, OR, NOT, uppercase), and quoted phrase matching in loose mode. |
search_term__exclude |
string | Search term to exclude. Recommended maximum 500 characters. |
buyer_name__is |
string | Buyer name to include. Recommended maximum 500 characters; very long values may exceed internal query limits. |
supplier_name__is |
string | Supplier name to include. Recommended maximum 500 characters; very long values may exceed internal query limits. |
buyer_address_country_name__is |
string | Country name of the buyer to include. |
buyer_address_country_name__exclude |
string | Country name of the buyer to exclude. |
buyer_address_country_code__is |
array[string] | Country codes of the buyer to include. |
buyer_address_country_code__exclude |
array[string] | Country codes of the buyer to exclude. |
ocid__is |
string | Open Contracting ID to include. |
language__is |
array[string] | Languages to include. |
language__exclude |
array[string] | Languages to exclude. |
tender_deadline__gte |
string($date-time) | Tender deadline greater than or equal to. |
tender_deadline__lte |
string($date-time) | Tender deadline less than or equal to. |
contract_start_date__gte |
string($date-time) | Contract start date greater than or equal to. |
contract_start_date__lte |
string($date-time) | Contract start date less than or equal to. |
contract_start_date__is |
string($date-time) | Contract start date exactly. |
contract_end_date__gte |
string($date-time) | Contract end date greater than or equal to. |
contract_end_date__lte |
string($date-time) | Contract end date less than or equal to. |
contract_end_date__is |
string($date-time) | Contract end date exactly. |
release_date__gte |
string($date-time) | Release date greater than or equal to. |
release_date__gt |
string($date-time) | Release date greater than. |
release_date__lte |
string($date-time) | Release date less than or equal to. |
release_date__lt |
string($date-time) | Release date less than. |
release_tags__is |
array[string] | Release tags to include - defined as an array with one or more of the tags published by publishers using OCDS (see below). |
tag_status__is |
string | Status provided by the publisher at publication. Do not use to find currently open notices; filter on tender_deadline__gte instead. |
cpv__is |
array[string] | CPV codes to include. |
dps_pred__is |
boolean | Whether the contract is a dynamic purchasing system contract. |
predicted_cpv_values |
boolean | Include predicted CPV values. |
cpv_aug_threshold |
float | Filter CPV augmented values by relevance score threshold (0-100). |
sort_by |
string | Sort field: release_date, date_created, date_updated, tender_end_date. Default: release_date. Does not apply when search_term__is is set: results are then ordered by relevance. |
date_direction |
string | Date sort direction (asc or desc). Default: desc. |
offset |
integer | Pagination offset (min: 0, max: 9900). |
limit |
integer | Number of records to return (min: 0, max: 10,000). There is no default. |
prefix_search |
boolean | Enable prefix search for CPV codes only , has no effect on search_term__is. When true, CPV codes in cpv__is are matched as prefixes (e.g. 45 matches all 45xxxxxx codes). |
value__gte |
integer | Minimum value (min: 0). Applies to the published tender or award value. |
value__gt |
integer | Greater than value (min: 0). |
value__lte |
integer | Less than or equal to value (min: 0). |
value__lt |
integer | Less than value (min: 0). |
currency__is |
string | Currency to query for (maxLength: 3, e.g., USD, GBP, EUR). |
dedupe__is |
boolean | true removes duplicate records we have identified. |
attachment__show |
boolean | Call to include original URLs and file names from attachments. |
attachment__is |
boolean | Show only results with attachments. |
search_language__is |
array[string] | Language codes that set the analyser used for stop words and stemming (e.g. ["en"], ["fr", "de"]). A single language uses that language's analyser. Where several languages are given and English is among them, the English analyser is used; otherwise the standard analyser is used. Only applies in strict and medium modes. |
search_mode |
string | Controls search strictness. "loose" (default): OR operator, 50% minimum match, fuzzy matching, supports Lucene syntax including quoted phrases and wildcards. Use wildcards (e.g. autobus*) to match inflected forms. "medium": all words required (AND), 75% minimum match, proximity boosting, fuzziness. "strict": exact phrase match, no fuzziness. |
Release Tags
Release tag items can be one or more of the following and expressed in an array. A small number of sources publish amendments as stand-alone documents; these arrive as separate records with an amendment release tag.
tender
award
planning
award contract
contract award
tenderUpdate
awardUpdate
tenderAmendment
planningUpdate
awardUpdate contractUpdate
contractUpdate awardUpdate
tender planning
tenderCancellation
planning tender
contractAmendment
contractTermination
planning award
planningUpdate award
award planning
award planningUpdate
Example Code
import requests
URL = "https://api.spendnetwork.cloud/api/v3/notices_summary/read_summary_records"
query = {
"release_tags__is": ["tender"],
"date_direction": "desc",
"offset": 0,
"limit": 50,
}
def fetch_summary_records(access_token):
try:
response = requests.post(
URL,
json=query,
headers={
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}",
},
)
response.raise_for_status()
return response.json()
except requests.RequestException as error:
print("Fetch error:", error)
return None
records = fetch_summary_records("YOUR_ACCESS_TOKEN")
print(records)
Authentication
To access the API users need to generate a bearer token. This is generated by making a call to the access-token endpoint /api/v3/login/access-token which uses an OAuth2 compatible login, and returns an access token for future requests. Supports both JSON and form data formats. The payload returns a token and the expiration date as follows. To refresh the token just call the access-token endpoint with your credentials.
curl -s -X POST "https://api.spendnetwork.cloud/api/v3/login/access-token" \
-H "Content-Type: application/json" \
-d '{"username":"your_email@example.com","password":"your_password"}'
Response:
{
"access_token": "eyJhbGciOiJIUzI.........",
"token_type": "bearer",
"expiration_date": "2024-12-31T23:59:59"
}
Replace "Bearer YOUR_ACCESS_TOKEN" with your actual API access token.
For detailed API documentation and additional options, visit the Spend Network API Swagger page.
Error Handling
When interacting with the API, it is important to handle potential errors gracefully. This includes checking for HTTP errors and handling exceptions appropriately.
def fetch_summary_records(access_token):
try:
response = requests.post(
"https://api.spendnetwork.cloud/api/v3/notices_summary/read_summary_records",
json=query,
headers={
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}",
},
)
response.raise_for_status()
return response.json()
except requests.HTTPError as http_err:
print(f"HTTP error occurred: {http_err}")
except requests.RequestException as error:
print("Fetch error:", error)
except Exception as err:
print(f"An error occurred: {err}")
return None
records = fetch_summary_records("YOUR_ACCESS_TOKEN")
print(records)
Example Request Bodies
Customise the request body for specific use cases to retrieve the desired data.
query = {
"date_direction": "asc",
"offset": 0,
"limit": 10,
"release_tags__is": ["award"],
"buyer_name__is": "MINISTRY OF DEFENCE",
}
Rate Limiting
New notices are published twice a day, typically at 6am and 11am GMT. Collect once after each publishing run; polling between runs returns no new data. Implement retry with exponential backoff rather than retrying immediately.
Pagination
To handle large datasets, use the offset and limit parameters to paginate results. Note that there is a 10,000 record limit on what can be returned from any single query.
query = {
"offset": 0,
"limit": 100 # Adjust as necessary, up to 10,000
}
while True:
result = fetch_summary_records("YOUR_ACCESS_TOKEN")
if not result or len(result.get("results", [])) < query["limit"]:
break
query["offset"] += query["limit"]
How to filter notices by CPV
This guide explains how to use:
cpv__isprefix_search
Filtering by CPV (**cpv__is**)
Send a list of CPV codes in the JSON request body:
{
"cpv__is": ["48000000", "72000000", "85000000"]
}
Prefix CPV matching (**prefix_search**)
When should you use prefix search?
Use prefix_search=true when you want to match a family of CPV codes.
Example:
- If you send
48000000withprefix_search=true, the API treats it like "anything starting with48". - That means codes like
48100000,48200000, etc. can match.
How to enable it
{
"cpv__is": ["48000000", "72000000"],
"prefix_search": true
}
What happens with trailing zeros
With prefix_search=true, the system removes trailing zeros to create the prefix:
48000000becomes prefix4872000000becomes prefix72
Sample responses
Here is an example of a typical response from the API:
{
"offset": 0,
"limit": 10,
"result_count": 1234,
"results": [
{
"tender_id": "tender_278767/1024151",
"buyer_name": "MINISTRY OF DEFENCE",
"tender_title": "The provision of End Point Assessment for Digital Apprenticeship Standards",
"release_date": "2021-12-17T08:27:49Z",
...
}
]
}
result_count is counted before deduplication, so it can exceed the number of records returned when dedupe__is is true.
Best Practices
- Optimize Queries: Use specific and relevant query parameters to limit the amount of data returned.
- Caching: Implement caching strategies to reduce the number of API calls and improve performance.
- Error Handling: Ensure robust error handling to manage API errors gracefully.
- Rate Limits: Be mindful of API rate limits and implement retry mechanisms where necessary.
- Pagination: Use pagination to handle large datasets efficiently.
API Limitations
- Record Limit: There is a 10,000 record limit on what can be returned from any query. Use pagination to retrieve additional records.
- Publishing Frequency: New notices are published twice a day, so ensure your application accounts for this cycle.
Contact Information
For further assistance or questions about the API, please contact our support team at contact@openopps.com.
Further code examples
Example Javascript code
The following JavaScript code demonstrates how to access the Spend Network summary records API. This script constructs a query body and sends an HTTP POST request to the endpoint. Replace TOKEN HERE with your actual API access token.
const URL = "https://api.spendnetwork.cloud/api/v3/notices_summary/read_summary_records";
const query = {
release_tags__is: ["tender"],
date_direction: "desc",
offset: 0,
limit: 50,
};
export const fetchSummaryRecords = async () => {
try {
const response = await fetch(URL, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: `Bearer TOKEN HERE`, // please add your sn-api access token
},
body: JSON.stringify(query),
});
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const body = await response.json();
return body;
} catch (error) {
console.error("Fetch error:", error);
return null;
}
};
Example Python code
The following Python code demonstrates how to access the Spend Network summary records API. This script constructs a query body and sends an HTTP POST request to the endpoint.
Replace "Bearer TOKEN HERE" with your actual API access token.
import requests
URL = "https://api.spendnetwork.cloud/api/v3/notices_summary/read_summary_records"
query = {
"release_tags__is": ["tender"],
"date_direction": "desc",
"offset": 0,
"limit": 50,
}
def fetch_summary_records(access_token):
try:
response = requests.post(
URL,
json=query,
headers={
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}",
},
)
response.raise_for_status()
return response.json()
except requests.RequestException as error:
print("Fetch error:", error)
return None
records = fetch_summary_records("TOKEN HERE")
print(records)
Document Attachments
Spend Network's summary API now supports both sourcing and downloading attachments (e.g. PDFs, DOCs) related to individual tender or contract documents. This feature allows users to programmatically access attachments, either via the original source information or through a direct download via Spend Network's servers.
Filtering For Attachments
Our API contains a boolean that shows when a document has attachments associated with it. Users can use the attachment__is feature in their query to return only documents with attachments, or alternatively only those without attachments.
Example Query Parameters
{
"attachment__is": true,
"date_direction": "asc",
"offset": 0,
"limit": 10,
"release_tags__is": ["award"],
"buyer_name__is": "MINISTRY OF DEFENCE"
}
Show Attachment Sources
In order to be able to provide a source URL for a given attachment we have an additional feature that can be called which adds the sourcing data to the document payload.
To retrieve document metadata (i.e. the titles and source URLs) for attachments associated with published tenders or contracts, API users can include the attachment__show parameter in your request for a specific document:
| Parameter | Type | Description |
|---|---|---|
attachment__show |
boolean (optional, default: false) |
When set to true, the response will include an attachments field with a list of attachment metadata. |
Example Query Parameters
Users should use the attachment__show parameter on a document by document basis to retain response time performance. Queries for multiple files will NOT be fast enough for production environments.
{
"attachment__show": true,
"ocid__is": "ocds-0c46vo-0001-12ef087c-3ad3-4d72-b2bb-e3d416081402",
}
Example Response Snippet
...
"attachments": [
{
"title": "Entwurfsplanung Heizung KG.pdf",
"url": "https://www.vergabe-westfalen.de/.../Entwurfsplanung+Heizung+KG.pdf"
},
{
"title": "LV Heizflächen Altbau.d83",
"url": "https://www.vergabe-westfalen.de/.../LV+Heizfl%C3%A4chen+Altbau.d83"
}
]
...
Downloading Attachments via Spend Network API
To simplify access to attachment files (bypassing source website restrictions), two dedicated endpoints are provided:
1. List Available Attachment Files
This allows users to list attachment files by title and url for a specific document.
Endpoint:
POST https://api.spendnetwork.cloud/api/v3/attachments/list
Authentication Required: Yes (Bearer token)
| Parameter | Type | Description |
|---|---|---|
source |
string |
Internal source identifier (e.g. td_vergabe_westfalen_de) |
tender_id |
string |
Unique tender ID (from the summary API) |
Example
The following request allows the user to return a list of attachments associated with a specific document.
curl -X POST \
'https://api.spendnetwork.cloud/api/v3/attachments/list' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"source": "td_vergabe_westfalen_de",
"tender_id": "41878101"
}'
Response
We use underscores (_) in filename values instead of period characters (.) to ensure compatibility with URL paths and to avoid issues with file extension parsing or routing logic in some systems.
[
{
"title": "SubmissionForm.docx",
"filename": "submissionform_docx"
}
]
Note: This filename value is required for the download endpoint.
2. Download Specific Attachment File
Endpoint Format:
GET /api/v3/attachments/download/{source}/{tender_id}/{filename}
Note: Use the
tender_idfield not theocididentifier.
Example
curl -X GET \
'https://api.spendnetwork.cloud/api/v3/attachments/download/td_vergabe_westfalen_de/41878101/submissionform_docx' \
-H 'Accept: application/json'
Behavior:
Returns the file as a direct download. In the event that the incorrect link has been used the API will return a Attachment file not found error.
How It Fits Together
- If
has_attachmentsistruefor a document, then you will get a response from the/attachments/listendpoint. - Get the
sourceandtender_idfrom the returned JSON, and check for available attachments using the/attachments/listend point. - Use
/attachments/listto retrieve a full list of filenames for that record. - Compile a query using the
source,tender_idandfilenamefrom memory and send a request to the/attachments/download/...end point to fetch a specific file by filename.
Best Practices
- Use
/attachments/listto get valid filenames for a specific document before calling/attachments/download. - Ensure that the
sourceandtender_idmatch what is returned from the summary API. - Always authenticate using your API token on both endpoints.
- Collect attachments on the basis of user demand, rather than in advance in aggregate.