SIMAP API: reading Swiss tenders and awards from simap.ch
simap.ch is the procurement platform shared by the Confederation, the cantons and the communes. Tenders, awards, direct awards and their corrections are published there. Since 1 July 2024 it has run on new software with a JSON REST API, and the endpoints that search and read publications answer without an account. In the week of 28 September to 2 October 2026, 570 procurement projects had a new publication on it: 306 tenders, 184 awards and 48 direct awards among them.
This guide is for developers who want those tenders and awards in their own system. Every request below was run on 3 October 2026, and the responses are trimmed from what the API returned that day. The award section ends with getting from an award to the winning company's UID, and from the UID to the company's commercial register entry.
What simap.ch publishes
A procurement project carries a sequence of publications. Each publication has a pubType, and the project keeps the same projectNumber throughout, with the publication numbered after it: 28727-01 is the tender, 28727-02 the award.
pubType | What it is | 28 Sep–2 Oct 2026 |
|---|---|---|
tender | Call for tenders in an open, selective or invitation procedure | 306 |
award | Award decision after a tender: winner, price, number of offers | 184 |
direct_award | Contract awarded without a call for tenders (freihändige Vergabe, gré à gré) | 48 |
abandonment, revocation | Procedure stopped or a publication withdrawn | 17, 0 |
advance_notice, request_for_information | Announcements before a tender | 4, 6 |
competition, study_contract, participant_selection | Design competitions, study contracts, the first stage of a selective procedure | 1, 0, 4 |
The counts are projects whose newest publication fell in that week, which is how the search counts them. Corrections are not a type of their own: a corrected tender is a new tender publication with corrected: true. 38 of the 570 were corrections.
Access: what needs an account
The OpenAPI specification is at https://www.simap.ch/api/specifications/simap.yaml (version 1.5.1 on 3 October 2026), with a Swagger UI at https://www.simap.ch/api-doc/ and a changelog beside it. The base URL is https://www.simap.ch/api. A test environment answers the same requests at https://int.simap.ch/api.
The specification declares OpenID Connect for every endpoint, but the read endpoints answer without a token: clause 6 of the API terms lists "Without authentication: access to publications, search for publications". Every request in this guide was sent without a token or a cookie.
| Task | Account needed |
|---|---|
| Search projects, read publications, look up awarded vendors and CPV codes | No |
| Download tender documents, ask questions in the Q&A forum, express interest | A user account linked to a registered vendor |
| Create and publish tenders | A user account in a procurement office |
| A machine client for the authenticated endpoints | Requested through the "Request for an API client for simap.ch (for productive and test environment)" form in the simap.ch FAQ |
We have not requested a client, so this guide covers only the public endpoints. The API is free today. The simap.ch association, which runs the platform, reserves the right to charge for some components with one month's notice, and to change the interface between versions. No rate limit is documented and the responses carry no rate-limit headers; our own import runs at about three requests per second without errors.
Search tenders
All searches go through GET /publications/v2/project/project-search. It needs at least one filter or a search term: a request with none answers 400 with E0003. Tenders whose works or services are located in canton Vaud, newest publication between 28 September and 2 October:
curl -s 'https://www.simap.ch/api/publications/v2/project/project-search?newestPubTypes=tender&orderAddressCantons=VD&newestPublicationFrom=2026-09-28&newestPublicationUntil=2026-10-02'
The response, trimmed to its first project:
{
"projects": [
{
"id": "f92055f2-a17b-48bc-8336-39ea3a966f70",
"title": { "de": null, "en": null, "fr": "Revitalisation du Ruisseau de Champmartin", "it": null },
"projectNumber": "38252",
"projectType": "tender",
"projectSubType": "construction",
"processType": "open",
"lotsType": "without",
"publicationId": "ab57fd7f-82f0-4c58-a5a2-788801d3ee8b",
"publicationDate": "2026-10-02",
"publicationNumber": "38252-01",
"pubType": "tender",
"corrected": false,
"procOfficeName": { "fr": "Commune de Vully-les-Lacs (Küng et associés SA)", ... },
"orderAddress": { "countryId": "CH", "cantonId": "VD", "postalCode": "1589 ", "city": { "fr": "Vully", ... } }
},
...
],
"pagination": { "lastItem": "20260930|45019", "itemsPerPage": 20 }
}
Every translated field comes as an object with de, fr, it and en, and most of them are filled in one or two languages only. Take the first non-null value, or the reader's language when it exists. The trailing space in "1589 " is in the source; trim postal codes before you compare them.
Parameters
| Parameter | Values | Note |
|---|---|---|
search | 3 to 1,000 characters | Full text in titles and descriptions. Two characters answer 400 (E0025). |
lang | de, fr, it, en | Languages the search term is matched in. All four when omitted. |
newestPubTypes | tender, award_tender, direct_award, advance_notice, … | Type of the project's newest publication. award_tender is the award of a tender. |
projectSubTypes | construction, service, supply, competition and study types | Works, services or supplies. |
processTypes | open, selective, invitation, direct, no_process | Procedure. |
cpvCodes, bkpCodes, npkCodes, … | Code values | A CPV parent matches its children (checked below). |
orderAddressCantons | ZH, VD, … | Canton where the work is done, when the office entered one. |
issuedByOrganizations | Office or institution UUIDs | Includes child offices. |
newestPublicationFrom, newestPublicationUntil | ISO dates | Inclusive. |
lastItem | From the previous page | Rolling pagination, 20 projects per page. |
Repeat a parameter to pass several values: newestPubTypes=tender&newestPubTypes=award_tender. The page size is fixed at 20, results come newest first, and the next page is the same request with lastItem set to the value from pagination. When a page holds fewer than 20 projects, you have reached the end.
Three filter behaviours that change your counts
Filters read the project's newest publication. newestPubTypes and the two date parameters test the latest publication of each project and nothing before it. A tender that was corrected on 2 October appears under 2 October even though it opened in June. A tender awarded since disappears from a newestPubTypes=tender search. And a newestPubTypes=award_tender search can return an abandonment: on a lots project where one lot was awarded and another abandoned, the project matched while the row's own pubType said abandonment. Check pubType on each row before you fetch it as an award. For a complete feed, sweep by date window without a type filter and sort the rows yourself.
CPV codes match downwards. In September 2026, cpvCodes=72000000 (IT services) returned 144 projects and cpvCodes=72200000 (software programming and consultancy) returned 44, all of them inside the 144. Pass the broadest code you want. GET /codes/v1/cpv?parentCode=72000000 lists the children with labels in four languages, also without a login.
The canton filter skips projects without a structured address. 206 of the 570 projects in the week above had no orderAddress.cantonId, because the office described the location in free text or not at all. orderAddressCantons drops those. To filter by buyer instead, use issuedByOrganizations or read procOfficeAddress.cantonId from the detail.
Read a tender
The search row gives you id (the project) and publicationId. The full publication is at GET /publications/v1/project/{projectId}/publication-details/{publicationId}. Its shape depends on type: tenders have dates, terms and criteria, awards have decision. The fields most integrations read:
| Need | Field path | Example (tender 37714-04) |
|---|---|---|
| Publication number and date | base.publicationNumber, base.publicationDate | 37714-04, 2026-10-02 |
| Title | base.title.{de,fr,it,en} | Kantonales Personenregister für GERES-Community |
| Main CPV code | procurement.cpvCode.code | 72200000 |
| Other codes | procurement.additionalCpvCodes[], procurement.bkpCodes[] | |
| Description | procurement.orderDescription.{lang} | HTML |
| Offer deadline | dates.offerDeadline | 2026-11-13T12:00:00+01:00 |
| Question rounds | dates.qnas[].date | 31 Jul, 14 Aug, 16 Oct 2026 |
| First publication of the tender | dates.initialPublicationDate | 2026-06-29 |
| Buying office | project-info.procOfficeAddress | Name, canton, email, phone |
| What a correction changed | correction.correctionDiffKeys[], correction.remarks | dates.qnas, project-info.documentsSourceUrl, … |
| Lots | lots[] | Empty when lotsType is without |
offerDeadline carries its UTC offset, so store it as a timestamp with time zone. Tender documents are behind a vendor login; the publication tells you where they are (project-info.documentsSourceType and documentsSourceUrl) and in which languages.
Read an award and find the winner's UID
An award publication names the winning vendors under decision.vendors. Here is the award of project 28727, published on 3 October 2026 by the building office of canton St.Gallen, trimmed:
curl -s 'https://www.simap.ch/api/publications/v1/project/941d4cae-f281-4546-a117-ac4fcde6bcb1/publication-details/9ce8c388-5fee-4877-85d5-4110a74599c7'
{
"type": "award",
"base": {
"publicationNumber": "28727-02",
"publicationDate": "2026-10-03",
"title": { "de": "Verwaltungsgebäude des Kantons St.Gallen Modernisierung Schliessanlagen - BKP 275", ... },
"processType": "open",
"lotsType": "without"
},
"procurement": {
"cpvCode": { "code": "45210000", "label": { "en": "Building construction work", ... } },
"bkpCodes": [ { "code": "275", "label": { "en": "Lock systems", ... } } ]
},
"decision": {
"vendors": [
{
"vendorId": "3e4fae92-e85b-4f6a-b58f-b41af6794540",
"vendorName": "Hasler + Co AG, die Zutrittsexperten",
"vendorAddress": { "countryId": "CH", "street": "Rudolf Diesel-Strasse 12", "city": "Winterthur", "postalCode": "8404", "cantonId": "ZH" },
"price": { "currency": "chf", "price": 1968451.1, "vatType": "full" },
"rank": null
}
],
"numberOfSubmissions": 6,
"awardDecisionDate": "2026-09-23"
},
"referencingPub": { "publicationNumber": "28727-01", "pubType": "tender", "publicationDate": "2025-12-15" }
}
The award carries no UID. It carries a vendorId, which is the key of the vendor's profile on simap.ch, and that profile is public:
curl -s 'https://www.simap.ch/api/vendors/v1/vendor/3e4fae92-e85b-4f6a-b58f-b41af6794540/public'
{
"id": "3e4fae92-e85b-4f6a-b58f-b41af6794540",
"name": "Hasler + Co AG, die Zutrittsexperten",
"uidNo": "CHE-100.766.120",
"dunsNo": null,
"url": "https://www.hasler.ch/?srv=sortiment&cmsrub=548",
"address": { "countryId": "CH", "city": "Winterthur", "postalCode": "8404", "cantonId": "ZH", ... },
"companySize": "large",
"isBiddingConsortium": false,
...
}
uidNo is the company's UID in its official dotted form. In the commercial register, CHE-100.766.120 is "Hasler + Co AG, Zutrittslösungen". The name on simap.ch is the vendor's own profile text, and here it differs from the registered name after the comma, so a join on names would have missed it. Join on the UID.
Coverage is high. In the first 80 award and direct-award publications of the week above, all 93 awarded vendors had a vendorId whose profile answered, and 85 of them had a UID: 85 of the 86 with a Swiss address. Across every award published on the new platform from July 2024 to 2 October 2026, as imported by Prospex, 95% of the vendors named in tender awards have a UID, and 89% of those named in direct awards. The rest are mostly foreign companies, which have a DUNS number or nothing, and bidding consortia (isBiddingConsortium: true), whose profile describes the consortium as a whole, without its members' UIDs.
Three more details matter for awards:
- Lots are awarded separately. On a project with lots, each lot's award is its own publication with a
lotobject (lotNumber,title) and its owndecision.vendors. Project 35277, a property-management mandate for the cantons of Fribourg and Vaud, published lot 1 to Basler & Hofmann SA on 2 October as publication 35277-02. - The price can be missing.
priceis optional, and some awards publish a range intotalPriceRangeinstead.vatTypenames the VAT treatment (full,reduced,no_vat, …). - The tender is linked backwards.
referencingPubpoints to the tender.GET /publications/v1/publication/{publicationId}/past-publicationsreturns the whole chain, but on a lots project it answers400unless you add?lotId=.
The vendor directory can also be searched directly: GET /vendors/v1?search=… matches name, UID or DUNS and returns uidNo on every row. It lists companies registered on simap.ch, which includes bidders that never won anything.
From the UID to the commercial register
With the UID you can read the rest of the company's record from the register. The Zefix REST API returns the registered name, legal form, seat, status and purpose for a UID, after you request credentials from the Federal Office of Justice. The federal UID register web service answers without registration, at up to 20 requests a minute. The SHAB API gives you the gazette notices behind every register change: a capital increase, a new board member, a move, a liquidation.
A short Python version of the award-to-UID path. It walks the pages of a search, skips the rows that are not awards, and reads the UID of each winner:
import time
import requests
API = "https://www.simap.ch/api"
session = requests.Session()
session.headers["User-Agent"] = "my-tender-monitor/1.0 (ops@example.ch)"
def search(**params):
"""Yield every project the search returns, page by page."""
while True:
page = session.get(f"{API}/publications/v2/project/project-search",
params=params, timeout=30).json()
yield from page["projects"]
if len(page["projects"]) < page["pagination"]["itemsPerPage"]:
return
params["lastItem"] = page["pagination"]["lastItem"]
time.sleep(0.5)
def award_winners(project):
"""The awarded vendors of one award publication, with their UID."""
detail = session.get(
f"{API}/publications/v1/project/{project['id']}"
f"/publication-details/{project['publicationId']}", timeout=30).json()
for vendor in detail["decision"]["vendors"]:
profile = session.get(
f"{API}/vendors/v1/vendor/{vendor['vendorId']}/public", timeout=30).json()
yield vendor["vendorName"], profile.get("uidNo"), (vendor.get("price") or {}).get("price")
for project in search(newestPubTypes=["award_tender"], cpvCodes=["72000000"],
newestPublicationFrom="2026-09-28",
newestPublicationUntil="2026-10-02"):
if project["pubType"] not in ("award", "direct_award"):
continue # the filter reads the project, so an abandoned lot can come back
title = next(t for t in project["title"].values() if t)
for name, uid, price in award_winners(project):
print(project["publicationNumber"], uid, name, price, title[:40])
Its output on 3 October 2026, IT-service awards of the week before:
22123-02 CHE-228.107.522 swisscharge.ch AG 6922324.0 E-Mobilität Backend-Anbieter und Abrechn
41852-02 CHE-110.136.653 Itesys AG 532000.0 AS_2026_062 Betrieb von MS-SQL-Servern a
21790-03 CHE-105.944.452 ELCA Informatique SA 320000.0 Projet informatique de gestion des ident
39517-02 CHE-101.654.423 Swisscom (Schweiz) AG 981305.0 Unified Communication and Collaboration
42894-02 CHE-105.952.747 BDO AG (Zürich) 257278.0 BYOD & Client Management
34583-02 CHE-105.318.036 Eraneos Switzerland AG 1229637.5 IT-Architekt für das Amt für Informatik
38723-02 CHE-107.410.894 Axpo Systems AG 325324.78 Ausschreibung «xWDM Multiplexer»
In production, cache vendor profiles by vendorId: many winners come back week after week.
Poll for new tenders
A publication dated today is readable from shortly after midnight: at 01:12 on Saturday 3 October 2026, a search with newestPublicationFrom=2026-10-03 already returned 20 projects. A daily job that keeps up looks like this:
- Search by date window, from the last run minus a few days through today, without a type filter. Re-reading the last days catches corrections, which move a project to a new date.
- Key every row on
publicationId. A newpublicationIdon a knownprojectNumberis a correction, an award or a withdrawal; read itspubTypeandcorrected. - Fetch the detail only for publications you have not stored yet. Keep the raw JSON; the API may add fields in a later version.
- For awards, fetch each
vendorIdonce and cache its profile.
For a tender alert, filter the stored rows by pubType == "tender" and your CPV codes, and compare dates.offerDeadline with today so expired tenders drop out. simap.ch also sends email alerts for up to ten saved searches per user account, which is enough to follow a few CPV codes without writing any code. The terms for those subscriptions limit them to personal or in-house use.
Terms of use
The GTC for the use of the simap.ch API (amended on 13 November 2023 for the new platform) allow commercial reuse and passing the data on to third parties, with conditions:
- The content must not be modified, and it must be visually distinct from your own comments.
- It must carry the statement: "This is not an official publication. The data published on the www.simap.ch platform are authoritative."
- Corrections and quality statements the association sends must be published too.
- Data may be passed to third parties from 8:00 on the day of publication. Your job can read earlier; an alert to customers waits until 8:00.
The German version of the terms prevails. This is a summary of what we read, not legal advice.
Notices from before July 2024
The new platform started empty on 1 July 2024; nothing was migrated from the old one. The old notices are on archiv.simap.ch, which has its own small API (OpenAPI at https://archiv.simap.ch/api/v3/api-docs): POST /api/search with a JSON body of filters, and GET /api/detail?meldungsnummer=… for one notice. It also answers without a login. A search for awards ("type_cd_ob": "OB02,OB08") published in June 2024 returned 1,272 notices. The archive's format is the old platform's: notice types are codes such as OB01 for a tender and OB02 for an award, and the awarded contractor comes with a name and address but no UID.
For federal contracts there is a second source. The Federal Procurement Conference publishes a yearly list of every contract of CHF 50,000 or more signed by the central federal administration, on opendata.swiss as "Beschaffungen ab 50 000 Franken der zentralen Bundesverwaltung". It includes contracts awarded without a publication on simap.ch, with supplier name, postcode, subject, procedure, contract dates and volume. It has no UID either, and the edition labelled 2024 holds the 2023 contracts.
Limits and pitfalls
- No list of all projects: a search needs a term or a filter. A date window is enough.
- 20 rows per page, no size parameter. A busy week is about 30 pages.
- Type and date filters test the newest publication only, as described above.
- A corrected publication is a new row; deduplicate on
publicationIdand group onprojectNumber. - Translated fields are partly empty. Fall back across languages.
- Descriptions are HTML with
and empty paragraphs. past-publicationson a lots project needslotId.- The vendor name is self-declared; the UID is the reliable key. Foreign vendors and consortia usually have no UID.
- The SPA pages under
/de/project-detailand similar are disallowed inrobots.txt. The/apipaths are not.
If you would rather not write the client, the open-source swissco CLI wraps the same public endpoints: swissco tenders lists projects by canton, date and publication type, and swissco vendor looks a company up in the vendor directory by name or UID. Where Swiss company data actually lives maps the register, the gazette and the other public sources a procurement record can be joined with. For a lighter read on what the platform publishes, see the strangest Swiss public tenders we found in it.
Frequently asked questions
- Does the SIMAP API need an account or an API key?
- Reading the SIMAP API needs no account or key: searching projects, reading tenders and awards, and looking up awarded vendors work without a login on www.simap.ch/api. Downloading tender documents and publishing tenders need a simap.ch user account.
- Where is the simap.ch API documentation?
- The Swagger UI is at www.simap.ch/api-doc and the OpenAPI file at www.simap.ch/api/specifications/simap.yaml, with a changelog beside it. Version 1.5.1 was live on 3 October 2026.
- How do I get the UID of the company that won a Swiss public tender?
- Read the award publication, take the vendorId of each awarded vendor, and request /vendors/v1/vendor/{vendorId}/public: its uidNo field holds the UID. In Prospex's import of awards since July 2024, 95% of the vendors named in tender awards have a UID. For that UID, the Prospex API returns the registered company with its canton, sector and size, and its events, such as capital increases and leadership changes.
- Can I reuse simap.ch data commercially?
- Yes. The GTC for the simap.ch API allow commercial use and passing data on to third parties from 8:00 on the day of publication, unmodified and with the statement that it is not an official publication.
- Where are simap.ch tenders from before July 2024?
- On archiv.simap.ch. The new platform started on 1 July 2024 without migrating old notices, and the archive has its own search API at archiv.simap.ch/api, also open without a login.