---
title: "Search Tender Awards"
method: GET
path: "/api/v1/tender_awards"
tags: ["tender_awards"]
---

# Search Tender Awards

`GET /api/v1/tender_awards`

## Request

- Base URL: `https://statsmapped.com`
- URL: `https://statsmapped.com/api/v1/tender_awards`
- Auth: one of:
  - HTTP bearer
  - none

Search awarded public-procurement contracts (migration 010's `tender_awards`).

Every filter is optional and AND-ed together. Results are ordered by
award_published_date desc, id desc (newest first, id as a stable tiebreaker for
rows sharing a date) -- the same ordering the cursor's keyset comparison is built
on, so pages never skip or repeat a row even if new data lands between requests
(the one thing an OFFSET-based page number cannot promise).

`supplier=` is the one exception to "fully open" -- see `_require_tender_supplier_scope`.
Independently of that filter, `awarded_supplier` itself is redacted (null) on every
row unless the caller is authorized -- security-privacy-auditor, 2026-08-28: an
unfiltered listing, paged with `cursor`, returned the field in full regardless, so
the whole supplier-name list was harvestable without ever using `supplier=` at
all. See `_tender_supplier_redaction_reason`.

## Query parameters

- `cpv_code` string, nullable: Matches any main_cpv_code starting with this value (CPV codes are hierarchical — e.g. '45' matches every construction-work code).
- `authority` string, nullable: Case-insensitive substring match on contracting_authority (free text, e.g. 'Health Service Executive').
- `local_authority_id` string, nullable: Exact geographies.id match (e.g. 'la:kerry-county-council') — populated for the ~31 city/county councils only; most rows have no single honest county home and are NULL, so this filter narrows to a strict subset of the table, never all of it.
- `supplier` string, nullable: Case-insensitive substring match on awarded_supplier. Requires a registered, manually-approved API key (Authorization: Bearer <key>) — see POST /api/v1/tender_awards/access-requests. Every other filter here is open without one.
- `date_from` string, date, nullable: award_published_date >= this.
- `date_to` string, date, nullable: award_published_date <= this.
- `value_min` number, nullable: awarded_value_eur >= this.
- `value_max` number, nullable: awarded_value_eur <= this.
- `single_bid_only` boolean: Only rows where bids_received = 1. journalist-workflow: the filter that makes 'single-bid contracts by council' answerable (combine with local_authority_id).
- `cursor` string, nullable: From a previous response's next_cursor; omit for page 1.
- `limit` integer

## Response `200`

Successful Response

- TenderAwardSearchResponse
  - `country` string
  - `results` TenderAward[], required
    - `country` string
    - `tender_id` string, required
    - `contracting_authority` string, required
    - `local_authority_id` string, nullable
    - `tender_name` string, nullable
    - `main_cpv_code` string, nullable
    - `main_cpv_code_description` string, nullable
    - `contract_type` string, nullable
    - `competition_type` string, nullable
    - `threshold_level` string, nullable
    - `notice_published_date` string, nullable
    - `award_published_date` string, required
    - `awarded_value_eur` number, required
    - `estimated_value_eur` number, nullable
    - `awarded_supplier` string, nullable
    - `bids_received` integer, nullable
    - `sme_bids_received` integer, nullable
  - `next_cursor` string, nullable
  - `total_count` integer, nullable

## Other responses

- `400`: Bad request
- `401`: Missing or invalid credentials
- `403`: Not authorized for this request
- `404`: Not found
- `422`: Validation error
- `429`: Rate limited

---

[API](https://skmtc.dev/statsmapped/apis/statsmapped-api.md) · [All operations](https://skmtc.dev/statsmapped/apis/statsmapped-api/llms.txt) · [OpenAPI document](https://skmtc.dev/statsmapped/apis/statsmapped-api/revisions/7c5b264d05b7?raw)
