Partner API
By requestRead access to the Schemes.sg catalogue, so partner organisations can surface the right scheme inside their own product.
https://asia-southeast1-schemessg.cloudfunctions.net/partner_apiGetting access
This is not a self-serve API. We work with a small number of partner organisations, and each key is issued by hand so we know who is using the data and can reach them if something changes.
Quick start
One request, to check your key works and see the shape of a scheme.
Authentication
Every request carries your key in a header. There is no token exchange, no OAuth dance, and no expiry. Send it as X-API-Key.
Base URL and versioning
The version is a path segment, so a future version can ship alongside this one without changing any URL you have already built against.
There is one version today. A request with no version, or an unknown one, returns 404 rather than quietly falling back to v1.
Operations
Three read operations. One key covers all of them.
List schemes
GET/v1/schemes
Page through active schemes. Pass at most one filter at a time. Results exclude retired and inactive schemes.
Query parameters
categorystringoptional- One of: Financial Assistance, Family & Children, Health & Wellbeing, Housing & Food, Education, Employment & Training, Seniors & Caregiving, Disability & Transport, Legal & Safety, Community Support. Case-insensitive, but the name must match exactly — a slug such as "financial-assistance" is rejected. Cannot be combined with agency or area. For example
Financial Assistance agencystringoptional- Filter by the agency that runs the scheme. Cannot be combined with category or area. For example
MSF areastringoptional- Filter by planning area. Cannot be combined with category or agency. For example
BEDOK limitintegeroptional- Schemes per page. Defaults to 10, capped at 50. For example
20 cursorstringoptional- Opaque cursor from a previous response's next_cursor. Pass it back exactly as received; a modified or truncated cursor returns 400 rather than silently restarting from the first page. Omit for the first page.
Retrieve a scheme
GET/v1/schemes/{scheme_id}
Fetch one scheme by id. Retired and inactive schemes return 404; a scheme retired by merging into another carries the new id.
Path parameters
scheme_idstringrequired- The scheme's id, as returned in scheme_id by list or search. For example
0kZ2mQ1xVbN4pR7t
Search schemes
POST/v1/schemes/search
Rank schemes against a natural-language description of someone's situation. Same retrieval the Schemes.sg assistant uses, without the conversational layer.
Body parameters
querystringrequired- Plain-language description of the situation, not keywords. Longer is better. For example
retired, living alone, struggling with medical bills limitintegeroptional- Results per page. Defaults to 20, capped at 50. For example
10 cursorstringoptional- Opaque cursor from a previous response's next_cursor. Pass it back exactly as received; a modified or truncated cursor returns 400 rather than silently restarting from the first page. Omit for the first page.
Scheme fields
Every scheme we return carries exactly these fields, whether it came from list, retrieve, or search. A field with no value is null rather than absent, so the set of keys never changes. A few fields hold either a single string or an array of strings, depending on the scheme — check the type column and handle both.
| Field | Type |
|---|---|
scheme_id | string |
scheme | string |
description | string |
summary | string |
eligibility | string |
who_is_it_for | string[] |
what_it_gives | string[] |
scheme_type | string[] |
agency | string |
link | string |
address | string | string[] |
phone | string | string[] |
email | string | string[] |
service_area | string |
planning_area | string | string[] |
image | string |
status | string |
We hold other fields internally, for review workflow and ranking. Those are deliberately not part of this contract, so our internal changes cannot break your integration.
Errors
Every failure returns the same envelope, so you can branch on error.code and never parse a message.
| Status | Code | What it means |
|---|---|---|
400 | invalid_request | The parameters are wrong. The message names which one. |
401 | missing_key | No X-API-Key header was sent. |
401 | invalid_key | The key is not one we issued. |
403 | revoked_key | The key was valid and has been turned off. Talk to us. |
404 | not_found | No such scheme, or it is no longer published. |
404 | scheme_retired | The scheme was retired and merged. The body carries merged_into. |
404 | unsupported_version | The version segment is missing or unknown. |
405 | method_not_allowed | Right path, wrong HTTP method. |
429 | rate_limited | Your per-minute budget is spent. Retry-After says when. |
500 | internal_error | Something broke on our side. Safe to retry. |
Rate limits
Each partner gets a per-minute budget, shared across all three operations: spending it on list also spends it for search. Every response tells you where you stand, so you never have to discover the limit by hitting it.
X-RateLimit-Limit- Your budget, in requests per minute.
X-RateLimit-Remaining- What is left in the current minute.
Retry-After- Seconds to wait. Only sent with a 429.
When a scheme goes away
Schemes close, and some are folded into a replacement. If you store scheme ids, handle this case: a retired scheme that was merged returns 404 with the id that replaced it, so you can follow the change instead of silently dropping the record.