# Rates API documentation > Rates API is a free JSON API for New Zealand interest rates. It has four datasets: mortgage, personal loan, car loan, and credit card rates, each with more than 30 lenders or issuers. It checks interest.co.nz each hour and keeps a maximum of one snapshot per day when the data changes, with history since 8 March 2025. Mortgage terms are from 6 months to 5 years, plus floating rates. An API key is not necessary. The source code is MIT licensed. The base URL is https://www.ratesapi.nz. All responses are JSON. The data comes from interest.co.nz and can be incorrect or late. Before you use a rate, check it with the lender. Rates API is independent. It is not affiliated with interest.co.nz or with any lender. --- # Overview URL: https://www.ratesapi.nz/docs Get New Zealand mortgage, personal loan, car loan, and credit card interest rates as JSON. Use the free Rates API with curl, OpenAPI, or MCP. [Rates API](https://www.ratesapi.nz/) gives the interest rates of New Zealand financial institutions. You can get the newest rates and snapshots of earlier rates. An API key is not necessary. All responses are JSON. The data can be incorrect. For correct rates, refer to the financial institution. For the data source and the terms of use, read [About](https://www.ratesapi.nz/docs/about). ## Key Facts Rates API is a free JSON API for New Zealand interest rates. It has four datasets: mortgage, personal loan, car loan, and credit card rates. Each dataset has more than 30 lenders or issuers. The API checks [interest.co.nz](https://www.interest.co.nz) each hour. It saves a maximum of one snapshot each day, when the data changes. The history starts on 8 March 2025. ## Start Here | Task | Page | | --------------------------------------------------------------------------- | ----------------------------------------------- | | Send your first request | [Quickstart](https://www.ratesapi.nz/docs/api-reference/quickstart) | | Connect an AI agent or an assistant | [AI Integration](https://www.ratesapi.nz/docs/api-reference/ai-integration) | | Read about IDs, dates, and errors | [Core Concepts](https://www.ratesapi.nz/docs/api-reference/concepts) | | Find the base URL, the endpoints, and the response rules | [API Reference](https://www.ratesapi.nz/docs/api-reference) | | Find all parameters and response fields, and send requests from the browser | [OpenAPI Reference](https://www.ratesapi.nz/openapi) | | Run or deploy the project | [Open Source](https://www.ratesapi.nz/docs/open-source) | | Read about the data source, the terms of use, and how to get help | [About](https://www.ratesapi.nz/docs/about) | ## Example ```bash curl https://www.ratesapi.nz/api/v1/mortgage-rates ``` The response contains a list of institutions. Each institution has products, and each product has rates. Use the `id` of an institution in detail requests and time-series requests. ## Made for AI Agents AI agents can find the data, get it, and check it without special work. | Item | How it helps an AI agent | | ---------------------------- | ----------------------------------------------------------------------------------------------- | | Model Context Protocol (MCP) | `POST /api/v1/mcp` gives tools that an MCP client can find and use. | | OpenAPI | `/openapi/json` gives a contract that tools can read. SDK generators can make clients from it. | | `llms.txt` | `/llms.txt` gives a short plain-text list of the documentation pages. | | JSON | Responses and errors always have the same shape. An agent can easily check a result. | | IDs | The ID of an institution or an issuer does not change. An agent can use it in the next request. | | No API key | An agent can use the data without secrets. | ## Data | Category | List Endpoint | Get By ID | Time Series | | ------------------- | --------------------------------- | ------------------------------------------------- | --------------------------------------------- | | Mortgage Rates | `GET /api/v1/mortgage-rates` | `GET /api/v1/mortgage-rates/{institutionId}` | `GET /api/v1/mortgage-rates/time-series` | | Personal Loan Rates | `GET /api/v1/personal-loan-rates` | `GET /api/v1/personal-loan-rates/{institutionId}` | `GET /api/v1/personal-loan-rates/time-series` | | Car Loan Rates | `GET /api/v1/car-loan-rates` | `GET /api/v1/car-loan-rates/{institutionId}` | `GET /api/v1/car-loan-rates/time-series` | | Credit Card Rates | `GET /api/v1/credit-card-rates` | `GET /api/v1/credit-card-rates/{issuerId}` | `GET /api/v1/credit-card-rates/time-series` | The mortgage data has fixed terms of 6 months to 5 years, and floating rates. ## Use Cases * Compare mortgages, personal loans, car loans, and credit cards. * Calculate the cost of a loan with the newest rates. * Show changes in the lending market on a dashboard. * Make charts of rates over time. The API keeps a snapshot only for days on which the data changed. * Examine rates over time with the same institution IDs. * Let an AI assistant answer questions about rates with data from the API. * Let an agent compare lenders or find rate changes. ## Summary | Item | Value | | -------------------------------- | -------------------------------------------------------------------------------- | | Base URL | `https://www.ratesapi.nz` | | Local API URL | `http://localhost:8787` | | Authentication | None. An API key is not necessary. | | Format | JSON | | OpenAPI reference (Scalar) | `/openapi` | | OpenAPI JSON | `/openapi/json` | | MCP endpoint | `POST /api/v1/mcp` | | Documentation list for LLMs | `/llms.txt` | | Full documentation text for LLMs | `/llms-full.txt` | | Source code | [github.com/simonbetton/ratesapi.nz](https://github.com/simonbetton/ratesapi.nz) | --- # API Reference URL: https://www.ratesapi.nz/docs/api-reference The base URL, endpoints, filters, errors, and time-series rules for New Zealand mortgage, personal loan, car loan, and credit card rates. Use this section when you know which data you must get. The [OpenAPI reference](https://www.ratesapi.nz/openapi) shows the parameters, the response fields, and the errors of each endpoint. If Rates API is new to you, start with the [Quickstart](https://www.ratesapi.nz/docs/api-reference/quickstart). Then read [AI Integration](https://www.ratesapi.nz/docs/api-reference/ai-integration) and [Core Concepts](https://www.ratesapi.nz/docs/api-reference/concepts). ## Base URL ```text https://www.ratesapi.nz ``` For local development, use this URL: ```text http://localhost:8787 ``` ## Authentication An API key is not necessary. ## OpenAPI Reference The OpenAPI reference shows all parameters, response fields, and errors of each endpoint. You can also send requests from it. | Resource | URL | | -------------------------- | -------------------------------- | | OpenAPI reference (Scalar) | [`/openapi`](https://www.ratesapi.nz/openapi) | | OpenAPI JSON | [`/openapi/json`](https://www.ratesapi.nz/openapi/json) | ## AI Integration | Resource | URL | Use | | --------------------------- | -------------------------------- | -------------------------------------------------------------- | | MCP endpoint | `POST /api/v1/mcp` | Find and use tools from an MCP client | | OpenAPI JSON | [`/openapi/json`](https://www.ratesapi.nz/openapi/json) | Make tools and SDKs, and examine the contract | | Documentation list for LLMs | [`/llms.txt`](https://www.ratesapi.nz/docs/llms.txt) | Give an assistant a plain-text list of the documentation pages | For more information about MCP, `llms.txt`, and agent patterns, read [AI Integration](https://www.ratesapi.nz/docs/api-reference/ai-integration). ## Endpoint Groups Each category has three endpoints: * The list endpoint gives the newest rates of all institutions or issuers. * The detail endpoint gives the newest rates of one institution or issuer. * The time-series endpoint gives snapshots of earlier rates. | Category | List Endpoint | Get By ID | Time Series | | ------------------- | --------------------------------- | ------------------------------------------------- | --------------------------------------------- | | Mortgage Rates | `GET /api/v1/mortgage-rates` | `GET /api/v1/mortgage-rates/{institutionId}` | `GET /api/v1/mortgage-rates/time-series` | | Personal Loan Rates | `GET /api/v1/personal-loan-rates` | `GET /api/v1/personal-loan-rates/{institutionId}` | `GET /api/v1/personal-loan-rates/time-series` | | Car Loan Rates | `GET /api/v1/car-loan-rates` | `GET /api/v1/car-loan-rates/{institutionId}` | `GET /api/v1/car-loan-rates/time-series` | | Credit Card Rates | `GET /api/v1/credit-card-rates` | `GET /api/v1/credit-card-rates/{issuerId}` | `GET /api/v1/credit-card-rates/time-series` | The health endpoint, `GET /api/v1/health`, shows if the API can read its database. For each dataset, it also shows the time of the last change, the time of the last successful data collection, and if the data collection stopped. ## Filters | Parameter | Endpoints | Result | | --------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | | `termInMonths` | All mortgage endpoints | Only the fixed rates for this term, for example, `12`. The response does not contain variable floating rates. | | `institutionId` | The time series of mortgages, personal loans, and car loans | Only the data of this institution | | `issuerId` | The time series of credit cards | Only the data of this issuer | The data contains mortgage terms of 6, 12, 18, 24, 36, 48, and 60 months. If you send a different term, the `products` arrays are empty. An endpoint does not use a filter that it does not accept. For example, the credit card time series does not use `institutionId`, and it gives the data of all issuers. Make sure that you send the correct filter for each endpoint. ## Responses All successful responses are JSON. Each rate response has a `lastUpdated` field. This field gives the date and time (UTC, ISO 8601) at which the API collected the data. An error response has this shape: ```json { "code": 400, "message": "Invalid request parameters" } ``` | HTTP status | Cause | Example message | | ----------- | ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | | 400 | A parameter is not correct. For example, a date is not in `YYYY-MM-DD` format. | `Invalid request parameters` | | 404 | The institution, the issuer, or the snapshot does not exist. | `Institution not found` | | 500 | An error occurred on the server. Send the request again. If the error continues, examine `/api/v1/health`. | `An error occurred while retrieving mortgage rates data` | ## Request IDs The API adds an `x-request-id` header to the responses of its endpoints. If you send an `x-request-id` header, the API returns the same value. If you do not send it, the API makes a new value. Use this value to find a request in the logs. Some responses do not have this header: * A response to a path that does not exist * A 400 response for a parameter of the incorrect type, for example, `termInMonths=abc` * The Scalar page at `/openapi` ## Time-Series Rules | Parameter | Description | | ----------- | ------------------------------------------------------------------------------------- | | `date` | The date of one snapshot, in `YYYY-MM-DD` format (UTC) | | `startDate` | The first date of a range, in `YYYY-MM-DD` format (UTC) | | `endDate` | The last date of a range, in `YYYY-MM-DD` format (UTC). The range contains this date. | Send `date`, or send `startDate` and `endDate` together. Do not send `date` with `startDate` or `endDate`. If you send no date, the response contains no snapshots. The `availableDates` field shows the dates that have a snapshot. --- # Quickstart URL: https://www.ratesapi.nz/docs/api-reference/quickstart Get New Zealand mortgage rates as JSON with one curl request. Filter by term, get the rates of one bank, and get earlier rates. No API key. Do these steps to get data from Rates API. An API key is not necessary. ## 1. Get the Newest Mortgage Rates ```bash curl https://www.ratesapi.nz/api/v1/mortgage-rates ``` The response contains a list of institutions. Each institution has products, and each product has rates. Keep the `id` of an institution for the steps that follow. ## 2. Get the Rates for One Term ```bash curl "https://www.ratesapi.nz/api/v1/mortgage-rates?termInMonths=12" ``` The `termInMonths` parameter gives only the fixed rates for one term. The data contains terms of 6, 12, 18, 24, 36, 48, and 60 months. The response does not contain variable floating rates. ## 3. Get the Rates of One Institution ```bash curl https://www.ratesapi.nz/api/v1/mortgage-rates/institution:anz ``` Use this endpoint for a page about one lender. You can also use it to update the data of one lender only. ## 4. Get One Snapshot ```bash curl "https://www.ratesapi.nz/api/v1/mortgage-rates/time-series?date=2026-04-30&institutionId=institution:anz&termInMonths=12" ``` The `date` parameter gives the snapshot of that date. If that date has no snapshot, the API returns HTTP 404. ## 5. Get the Snapshots in a Range ```bash curl "https://www.ratesapi.nz/api/v1/mortgage-rates/time-series?startDate=2026-04-01&endDate=2026-04-30&institutionId=institution:anz" ``` Send `startDate` and `endDate` together. The range contains the two dates. Do not send `date` with a range. The API keeps a snapshot only when the data changes. Because of this, some dates in a range have no snapshot. The `availableDates` field shows the dates that have a snapshot. ## 6. Examine the API in the Browser Open [`/openapi`](https://www.ratesapi.nz/openapi) to send requests from the Scalar API client. To get the OpenAPI document, use [`/openapi/json`](https://www.ratesapi.nz/openapi/json). ## 7. Connect an AI Agent If your agent can use the Model Context Protocol (MCP), send requests to the MCP endpoint: ```text POST https://www.ratesapi.nz/api/v1/mcp ``` To give an assistant a list of the documentation pages, use [`/llms.txt`](https://www.ratesapi.nz/docs/llms.txt). To get the request and response schemas, use [`/openapi/json`](https://www.ratesapi.nz/openapi/json). Some AI frameworks can make tools from this OpenAPI contract. ## Next Steps * Read [AI Integration](https://www.ratesapi.nz/docs/api-reference/ai-integration) for MCP, OpenAPI, and `llms.txt`. * Read [Core Concepts](https://www.ratesapi.nz/docs/api-reference/concepts) for IDs, dates, errors, and data freshness. * Use the [OpenAPI reference](https://www.ratesapi.nz/openapi) to find all parameters and response fields of each endpoint. * To run or deploy the project, read [Open Source](https://www.ratesapi.nz/docs/open-source). --- # AI Integration URL: https://www.ratesapi.nz/docs/api-reference/ai-integration Connect AI agents to New Zealand mortgage and loan rates. Use the free MCP endpoint, the OpenAPI JSON for tool generation, and llms.txt. You can use Rates API in AI products without a private integration. The API gives documentation that machines can read, JSON responses with a fixed shape, an MCP endpoint, and a plain-text list of the documentation pages. Use this page when you add Rates API to an AI product. Examples are chat assistants, research agents, search systems, workflows, and applications that call tools. ## Integration Items | Item | URL | Use | | --------------------------- | ---------------------------------- | ------------------------------------------------------------------- | | MCP endpoint | `POST /api/v1/mcp` | Agents that use the Model Context Protocol (MCP) | | Scalar OpenAPI reference | [`/openapi`](https://www.ratesapi.nz/openapi) | Find endpoints and send test requests from the browser | | OpenAPI JSON | [`/openapi/json`](https://www.ratesapi.nz/openapi/json) | Make SDKs and agent tools, and check the contract | | Documentation list for LLMs | [`/llms.txt`](https://www.ratesapi.nz/docs/llms.txt) | A plain-text list of the documentation pages, with titles and links | | Full documentation for LLMs | [`/llms-full.txt`](https://www.ratesapi.nz/docs/llms-full.txt) | The full text of all documentation pages in one Markdown file | | Search API | `/api/search` | Search in this documentation site | ## Recommended Architecture | Step | Component | Function | | ---- | ------------------------------- | -------------------------------------------------------------------------------- | | 1 | User question | A person asks for the newest rates, earlier rates, or a comparison. | | 2 | AI assistant or workflow | The agent finds which data is necessary: documentation, endpoint data, or rates. | | 3 | `/llms.txt` and `/openapi/json` | The agent finds the endpoints, the parameters, and the response shapes. | | 4 | `POST /api/v1/mcp` | An MCP agent finds the Rates API tools and uses them. | | 5 | `/api/v1/*` JSON endpoints | Direct tools and the MCP tools get the rates. | | 6 | Answer | The agent gives an answer that uses API data and IDs. | Use OpenAPI and `llms.txt` to tell the agent which items are available. Use MCP if your agent runtime can find tools and call them. Send direct `/api/v1/*` requests if you make tools or a typed client. ## MCP Endpoint The MCP endpoint is at this URL: ```text POST https://www.ratesapi.nz/api/v1/mcp ``` The endpoint uses the Streamable HTTP transport and JSON-RPC 2.0. It accepts only `POST` requests. A `GET` or `DELETE` request gets HTTP 405. ### Protocol Versions | Version | Type | How a client uses it | | ---------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------- | | `2026-07-28` | Current | Each request contains its protocol version. An `initialize` request is not necessary. | | `2025-11-25`, `2025-06-18`, `2025-03-26`, and `2024-11-05` | Legacy | The client sends `initialize` first. If the server supports the requested version, it uses that version. If not, it uses `2025-11-25`. | To get the list of supported versions, send `server/discover`. If you send a version that the server does not support, the server returns HTTP 400 and error `-32022`. The error data contains the list of supported versions. The server does not use sessions. It does not send an `Mcp-Session-Id` header. ### Connect a Client The MCP server does not use an API key or OAuth. Give the endpoint URL to your MCP client. #### Claude Code Run this command: ```bash claude mcp add --transport http ratesapi https://www.ratesapi.nz/api/v1/mcp ``` To share the server with your team, add it to the `.mcp.json` file at the root of your project: ```json { "mcpServers": { "ratesapi": { "type": "http", "url": "https://www.ratesapi.nz/api/v1/mcp" } } } ``` #### Claude and Claude Desktop Add Rates API as a custom connector: 1. Go to **Customize > Connectors**. 2. Select **+**, then select **Add custom connector**. 3. Type `https://www.ratesapi.nz/api/v1/mcp` as the remote MCP server URL. Then select **Add**. Claude Desktop does not read remote servers from `claude_desktop_config.json`. Use a custom connector. #### Cursor Add the server to `.cursor/mcp.json` in your project. To use it in all projects, add it to `~/.cursor/mcp.json`: ```json { "mcpServers": { "ratesapi": { "url": "https://www.ratesapi.nz/api/v1/mcp" } } } ``` ### Requests for 2026-07-28 Each request must contain these items: * In `params._meta`, the `io.modelcontextprotocol/protocolVersion` and `io.modelcontextprotocol/clientCapabilities` fields * The `MCP-Protocol-Version` header, with the same version as `_meta` * The `Mcp-Method` header, with the same value as `method` * For `tools/call`, the `Mcp-Name` header, with the same value as `params.name` If a header is missing or does not match the body, the server returns HTTP 400 and error `-32020`. If a `_meta` field is missing, the server returns HTTP 400 and error `-32602`. | Method | Function | | ----------------- | ------------------------------------------------------------------------------------------------- | | `server/discover` | Gives the supported versions, the capabilities, the server information, and instructions for LLMs | | `tools/list` | Gives the list of Rates API tools and their input schemas | | `tools/call` | Uses one tool with the arguments that you send | Each result contains `resultType: "complete"` and the server information in `_meta`. The results of `server/discover` and `tools/list` also contain `ttlMs` and `cacheScope`. A client can keep these results for one hour. An unknown method gets HTTP 404 and error `-32601`. The `ping` method does not exist in `2026-07-28`. ### Legacy Requests A legacy client sends `initialize`, then `tools/list` and `tools/call`. The server also accepts `ping`. Clients for `2025-06-18` and later send the `MCP-Protocol-Version` header with the negotiated version. The server uses a request without this header as a `2025-03-26` request. Such a request can also be a JSON-RPC batch. A notification (a message without an `id`) gets HTTP 202 with no body. ### Tools | Category | Tools | | -------------- | ----------------------------------------------------------------------------------------------------------- | | Mortgages | `list_mortgage_rates`, `get_mortgage_rates_by_institution`, `get_mortgage_rates_time_series` | | Personal loans | `list_personal_loan_rates`, `get_personal_loan_rates_by_institution`, `get_personal_loan_rates_time_series` | | Car loans | `list_car_loan_rates`, `get_car_loan_rates_by_institution`, `get_car_loan_rates_time_series` | | Credit cards | `list_credit_card_rates`, `get_credit_card_rates_by_issuer`, `get_credit_card_rates_time_series` | Each tool has a title and the `readOnlyHint: true` annotation. The tools only read data. All tool arguments are strings. For example, send `"termInMonths": "12"`, not `"termInMonths": 12`. ### Example Tool Call ```bash curl https://www.ratesapi.nz/api/v1/mcp \ -H 'Content-Type: application/json' \ -H 'MCP-Protocol-Version: 2026-07-28' \ -H 'Mcp-Method: tools/call' \ -H 'Mcp-Name: list_mortgage_rates' \ -d '{ "jsonrpc": "2.0", "id": "rates-1", "method": "tools/call", "params": { "name": "list_mortgage_rates", "arguments": { "termInMonths": "12" }, "_meta": { "io.modelcontextprotocol/protocolVersion": "2026-07-28", "io.modelcontextprotocol/clientCapabilities": {} } } }' ``` The result contains one `text` item. The text is the same JSON that the related REST endpoint returns. For `2025-06-18` and later, `structuredContent` also contains this JSON. If the tool fails, the result contains `isError: true`. For `2025-11-25` and later, an incorrect argument also gives a result with `isError: true`. The model can then read the error and correct the argument. ### Errors | Code | Message | Cause | | -------- | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | | `-32700` | `Parse error` | The request body is not JSON. The HTTP status is 400. | | `-32600` | `Invalid Request` | The body is not a JSON-RPC 2.0 request. | | `-32601` | `Method not found` | The method does not exist. For versions before `2025-11-25`, an unknown tool also gives this error, with the message `Tool not found`. | | `-32602` | `Invalid params` | A `_meta` field is missing, or the tool does not exist. For versions before `2025-11-25`, an incorrect argument also gives this error. | | `-32603` | `Internal error` | An error occurred on the server. | | `-32020` | `Header mismatch` | A necessary header is missing, or it does not match the body. The HTTP status is 400. | | `-32022` | `Unsupported protocol version` | The server does not support the version. The HTTP status is 400. | The MCP server sends `listChanged: false`, because the list of tools does not change while the API version stays the same. The rate data can change each hour. ## OpenAPI for Tool Generation Use [`/openapi/json`](https://www.ratesapi.nz/openapi/json) if your AI framework can change OpenAPI operations into tools. The OpenAPI document contains the `/api/v1/*` data endpoints and the MCP endpoint, `POST /api/v1/mcp`. The MCP operation (`sendMcpMessage`) shows the headers, the JSON-RPC message schemas, and the tool names. This page gives all the MCP information. If your framework makes a tool from each OpenAPI operation, do not make a tool from `sendMcpMessage`. To use MCP, connect an MCP client to the MCP endpoint. ## Documentation List for LLMs Use [`/llms.txt`](https://www.ratesapi.nz/docs/llms.txt) when an assistant, a coding agent, or a search system must have a short plain-text list of the documentation. `/llms.txt` shows each documentation page as a Markdown link, with its title and description. It also shows the OpenAPI document, the MCP endpoint, and the data endpoints. It does not contain the full text of the pages. To read a page, get the linked page. For request schemas and response schemas, use [`/openapi/json`](https://www.ratesapi.nz/openapi/json). To get the full text of all pages in one file, use [`/llms-full.txt`](https://www.ratesapi.nz/docs/llms-full.txt). Use `/llms.txt` for these tasks: * Add a list of the Rates API pages to the system prompt of an assistant. * Let a search system find the available pages before it gets the necessary pages. * Give a coding agent a list of pages to read. * Keep the documentation separate from the rate data. ## API Rules That Help Agents | Rule | Result | | ------------------------------------------------- | ------------------------------------------------------------------------ | | An API key is not necessary | Agents can make prototypes and tests without secrets. | | The IDs of institutions and issuers do not change | The next tool call can use the same IDs. | | JSON responses have a fixed shape | Tool results are easy to parse, check, and show. | | Errors have a fixed shape | An agent can process incorrect tool arguments in the same way each time. | | REST responses have an `x-request-id` header | You can connect agent logs to API logs. | | Time-series filters | Agents can answer questions about earlier rates and trends. | ## Example Agent Tasks * Compare the newest 12-month mortgage rates of New Zealand lenders. * Find the newest rates of one institution before you write a reply to a customer. * Make a daily summary when a monitored rate changes. * Answer questions about earlier rates, for example, "What did ANZ offer last month?" * Add the newest lending rates to a workflow for financial plans. ## Select an Integration Path | If you make this | Start with | | -------------------------------- | --------------------- | | An MCP agent | `POST /api/v1/mcp` | | An SDK or a typed client | `/openapi/json` | | A chat assistant that reads docs | `/llms.txt` | | A prototype in the browser | `/openapi` | | A product feature | `/api/v1/*` endpoints | --- # Core Concepts URL: https://www.ratesapi.nz/docs/api-reference/concepts How Rates API collects New Zealand rates from interest.co.nz each hour, and how snapshots, lender IDs, date filters, and errors operate. ## Data Freshness The API collects data from interest.co.nz each hour. It saves a dataset only when the data changes. Cloudflare D1 keeps the newest version of each dataset and snapshots of earlier versions. To see the freshness of each dataset, send a request to the health endpoint: ```bash curl https://www.ratesapi.nz/api/v1/health ``` For each dataset, the health response has these fields: | Field | Value | | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `lastUpdated` | The time of the last change to the data. | | `lastChecked` | The time of the last successful data collection, also when the data did not change. The value is `null` if the time is not known. | | `stale` | `true` if no data collection was successful in the last 3 hours. `false` if a data collection was successful in the last 3 hours. `null` if `lastChecked` is `null`. | In the health response, `lastUpdated` and `lastChecked` have the format `YYYY-MM-DD HH:MM:SS` (UTC). In the rate responses, `lastUpdated` has the ISO 8601 format. An old `lastUpdated` value has two possible causes: the rates did not change, or the data collection failed. To find the cause, examine `stale`. If `stale` is `false`, the data collection operates correctly and the rates did not change. ## Identifiers Use the IDs from the list endpoints in detail requests and time-series requests. | Identifier | Endpoints | Example | | --------------- | ----------------------------------------------- | ----------------- | | `institutionId` | Mortgage, personal loan, and car loan endpoints | `institution:anz` | | `issuerId` | Credit card endpoints | `issuer:amex` | The ID of an institution or an issuer does not change when the API collects new data. You can use upper-case or lower-case letters in an ID. Product names and products can change when an institution changes its rates page. Product IDs and rate IDs identify items in a response. No endpoint uses them as a parameter. ## Newest Data and Earlier Data | Data | Endpoint | | ----------------------------------------------- | ------------------------------------------------------------------------ | | The newest rates of all institutions or issuers | A list endpoint, for example, `/api/v1/mortgage-rates` | | The newest rates of one institution or issuer | A detail endpoint, for example, `/api/v1/mortgage-rates/{institutionId}` | | One snapshot | A time-series endpoint with `date` | | All snapshots in a range | A time-series endpoint with `startDate` and `endDate` | | The dates that have a snapshot | A time-series endpoint with no date | The API keeps a maximum of one snapshot for each dataset on each UTC date. If the data changes again on the same date, the new snapshot replaces the earlier snapshot. A date on which the data did not change has no snapshot. On that date, the rates were the same as in the snapshot before it. Use the `availableDates` array in each time-series response to find the dates that have data. Some days do not have a snapshot. ## Date Filters Time-series endpoints use calendar dates in `YYYY-MM-DD` format (UTC). Use either `date` or the `startDate` and `endDate` pair. Do not send `date` with `startDate` or `endDate`. | Parameters | Result | | ------------------------------------ | ------------------------------------------------------------------------------ | | `date` only | One snapshot | | `startDate` and `endDate` | All snapshots in the range. The range contains the two dates. | | No date | No snapshots. The `availableDates` field shows the dates that have a snapshot. | | `startDate` without `endDate` | Invalid request | | `endDate` without `startDate` | Invalid request | | `date` with `startDate` or `endDate` | Invalid request | | `startDate` after `endDate` | Invalid request | An invalid request returns HTTP 400: ```json { "code": 400, "message": "Invalid request parameters" } ``` If `startDate` is after `endDate`, the message is `Start date cannot be after end date`. If the API has no snapshot for the request, it returns HTTP 404. For example, the message can be `No data available for date: 2020-01-01`. ## Request IDs The API adds an `x-request-id` header to the responses of its endpoints. If a client sends `x-request-id`, the API returns the same value. If the client does not send it, the API makes a new value. A response to a path that does not exist does not have this header. A 400 response for a parameter of the incorrect type also does not have it. ## CORS The `/api/v1/*` endpoints accept requests from all origins. A preflight response contains `access-control-max-age: 600`. A browser can keep the preflight result for 600 seconds. ## AI Integration Items | Item | Use | | ------------------ | ------------------------------------------------------------------------------ | | `POST /api/v1/mcp` | The MCP endpoint. Clients use it to find tools and to use tools. | | `/openapi/json` | A REST contract that SDK generators and tool generators can read | | `/llms.txt` | A plain-text list of the documentation pages for assistants and search systems | | `/api/v1/*` | JSON data endpoints for direct requests | The MCP server sends `listChanged: false`. This value means that the list of tools does not change while the API version stays the same. The rate data can change. ## OpenAPI The Scalar reference is at `/openapi`, and the OpenAPI document is at `/openapi/json`. The OpenAPI document also contains the MCP endpoint, `POST /api/v1/mcp`, with the `MCP` tag. --- # Open Source URL: https://www.ratesapi.nz/docs/open-source How the open source New Zealand rates API is built: Cloudflare Workers, D1, Elysia, hourly scrapers, and the repository layout. MIT licensed. Rates API is an open source project that operates on Cloudflare Workers. The repository contains the API Worker, the documentation app, the landing page, the scraper scripts, the tests, and the GitHub Actions workflows. ## Architecture | Component | Function | | ------------------- | -------------------------------------------------------------------------------------------------------------------- | | API Worker | Serves `/api/v1/*`, `/openapi`, and `/openapi/json` | | Docs Worker | Serves this Fumadocs site at `www.ratesapi.nz/docs` | | Landing page Worker | Serves the TanStack Start app in `apps/web` at `www.ratesapi.nz`. It sends a redirect from the apex domain to `www`. | | Cloudflare D1 | Keeps the newest datasets and the historical snapshots | | Scraper scripts | Collect rates from the interest.co.nz pages | | GitHub Actions | Do checks, builds, data collection, deployment, and uptime checks | ## Technology Stack | Layer | Tools | | ------------- | ----------------------------------------------------------------------- | | Runtime | Bun and Cloudflare Workers | | API routes | Elysia | | Schemas | Elysia `t` and TypeBox validation at runtime | | API reference | The Elysia OpenAPI plugin with Scalar | | Docs site | Next.js, Fumadocs UI, and Fumadocs MDX | | Landing page | TanStack Start, React, and Tailwind CSS | | Data storage | Cloudflare D1 | | Scrapers | Cheerio | | Code quality | Oxlint, Oxfmt, TypeScript (`tsgo`), Fallow, React Doctor, and Bun tests | ## Repository Structure | Path | Contents | | ------------------------- | ------------------------------------------------------------------------ | | `apps/api/src/` | The code of the API Worker | | `apps/api/src/routes/` | The Elysia route groups | | `apps/api/src/models/` | The runtime schemas and the TypeScript types | | `apps/api/src/lib/` | Shared API functions | | `apps/api/bin/` | The scraper scripts, the D1 scripts, and the uptime script | | `apps/api/test/` | The API, scraper, and data loader tests | | `apps/docs/app/` | The Next.js App Router routes of the docs site | | `apps/docs/content/docs/` | The MDX documentation pages | | `apps/docs/test/` | The docs content tests | | `apps/web/` | The landing page | | `.github/workflows/` | The CI, deploy, scraping, monitoring, Fallow, and React Doctor workflows | ## Changes to the Project * API changes: Change the route handlers, the schemas, the OpenAPI descriptions, and the contract tests together. * Scraper changes: Change the scraper normalization, the validation, and the D1 write behavior together. * Documentation changes: Change the MDX files in `apps/docs/content/docs`. Then run `bun run check`. * Deployment changes: Change the Worker configuration and the CI build job together. Write the OpenAPI descriptions and the documentation pages in ASD-STE100 Simplified Technical English. To start, read [Local Development](https://www.ratesapi.nz/docs/open-source/local-development). To operate a copy of the service, read [Deployment](https://www.ratesapi.nz/docs/open-source/deployment). --- # Local Development URL: https://www.ratesapi.nz/docs/open-source/local-development Clone Rates API, install it with Bun, start the API, docs, and landing page, and load real New Zealand rate data into a local D1 database. This project is a Bun workspace with three apps. The root scripts run a task in each app. Each app has scripts in its `package.json`. | App | Path | URL | | ------ | ----------- | ---------------------------- | | `api` | `apps/api` | `http://localhost:8787` | | `docs` | `apps/docs` | `http://localhost:3000/docs` | | `web` | `apps/web` | `http://127.0.0.1:3002` | ## Prerequisites * [Bun](https://bun.sh/): The JavaScript runtime and package manager of the project * Git * A text editor, for example, VS Code * A Cloudflare account, only for remote D1 access or deployment ## Procedure ### 1. Clone the Repository ```bash git clone https://github.com/simonbetton/ratesapi.nz.git cd ratesapi.nz ``` ### 2. Install the Dependencies ```bash bun i ``` One install at the root of the repository installs the dependencies of all apps. ### 3. Start All Apps ```bash bun run dev ``` This command starts the API, the docs, and the web apps together. The API uses a local D1 database on your computer. A Cloudflare account is not necessary. Each time the API starts, it makes the tables that do not exist. Then it adds sample data: one provider for each category, with a snapshot on 2026-04-30. The sample data does not replace rows that you already have. ## Run One App To run the script of one app, use `--filter` with the name of the app: ```bash bun run --filter docs dev ``` ## Get Rate Data The sample data is small. To get the newest rates into your local D1 database, run the scraper scripts: ```bash bun run --filter api scrape:local ``` The scripts collect the rates from interest.co.nz and save them in the local database. The data stays when you start the API again. The scraper scripts are in `apps/api/bin/`: * `scrape-mortgage-rates.ts`: Collects mortgage rates * `scrape-personal-loan-rates.ts`: Collects personal loan rates * `scrape-car-loan-rates.ts`: Collects car loan rates * `scrape-credit-card-rates.ts`: Collects credit card rates Each scrape adds a maximum of one snapshot for each day. The sample snapshot of 2026-04-30 also stays in the history. ### Remote D1 Data To use the remote D1 database, log in to Cloudflare. Then run this command: ```bash bun run --filter api dev:remote ``` This command gives the full production data and the full history. > **WARNING:** The `development` environment in `apps/api/wrangler.toml` has the same database ID as production. As a result, `dev:remote` connects to the production database. The API endpoints only read data. Do not run scripts that write data with this connection. ## Checks Before you open a pull request, run all checks: ```bash bun run check ``` This command runs Oxlint and Oxfmt, does a type check of each app, and runs the Bun tests. To build each app in the same way as CI, run this command: ```bash bun run build ``` ## Frequent Tasks | Task | Files | | --------------------------- | ---------------------------------------------------------------------------------------- | | Add an endpoint | `apps/api/src/routes/`, `apps/api/src/models/`, and `apps/api/test/api-contract.test.ts` | | Change the documentation | `apps/docs/content/docs/` | | Change the scraper behavior | `apps/api/bin/` and `apps/api/src/models/` | | Change the deployment | `apps/api/wrangler.toml`, `apps/docs/wrangler.toml`, and `.github/workflows/` | --- # Deployment URL: https://www.ratesapi.nz/docs/open-source/deployment Deploy your own New Zealand interest rates API to Cloudflare Workers: prepare D1, set the Worker routes, build, and set GitHub Actions secrets. Rates API deploys three Cloudflare Workers on `www.ratesapi.nz`: | Worker | App | Serves | | ------------------ | ----------- | --------------------------------------------------------- | | `ratesapi-nz` | `apps/api` | The API routes: `/api/v1/*`, `/openapi`, and `/openapi/*` | | `ratesapi-nz-docs` | `apps/docs` | The Fumadocs site at `/docs` and `/docs/*` | | `ratesapi-nz-web` | `apps/web` | The landing page, and all other paths | Cloudflare D1 keeps the newest datasets and the historical snapshots for the API Worker. ## Prerequisites 1. A [Cloudflare account](https://dash.cloudflare.com/sign-up) 2. The project, prepared for local development 3. [Bun](https://bun.sh/) 4. The [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/install-and-update/) for Cloudflare Workers and D1 ## Cloudflare D1 Do these steps before you deploy the API Worker for the first time. 1. Log in to Cloudflare: ```bash wrangler login ``` 2. Go to the API app: ```bash cd apps/api ``` 3. Add the D1 database to your Cloudflare account: ```bash wrangler d1 create ratesapi-data ``` 4. Copy the database ID from the output. Write it in the two `d1_databases` blocks in `apps/api/wrangler.toml`. 5. Add the tables to the remote database: ```bash wrangler d1 execute ratesapi-data --remote --file=schema.sql ``` > **CAUTION:** Without `--remote`, Wrangler changes only the local database on your computer. ## Worker Configuration The landing page Worker is the origin for `www.ratesapi.nz` and the apex domain `ratesapi.nz`. The configuration is in `apps/web/wrangler.toml`: ```toml [[routes]] pattern = "www.ratesapi.nz" custom_domain = true [[routes]] pattern = "ratesapi.nz" custom_domain = true ``` The API Worker and the docs Worker use route patterns. Cloudflare runs a route Worker before the Worker of a Custom Domain. Thus these Workers get their paths before the landing page Worker. The configuration of the docs Worker is in `apps/docs/wrangler.toml`. Next.js serves the docs with `basePath: "/docs"`: ```toml [[routes]] pattern = "www.ratesapi.nz/docs*" zone_name = "ratesapi.nz" ``` The `*` at the end of the pattern matches `/docs`, `/docs` with a query string, and all paths under `/docs/`. A pattern without `*` does not match a URL that has a query string. The docs Worker serves the prerendered pages from Workers static assets. It does not render the pages again for each request. The configuration of this cache is in `apps/docs/open-next.config.ts`. The configuration of the API Worker is in `apps/api/wrangler.toml`. The API Worker controls the API paths on `www.ratesapi.nz` and on the apex domain: ```toml [[routes]] pattern = "www.ratesapi.nz/api/v1/*" zone_name = "ratesapi.nz" [[routes]] pattern = "ratesapi.nz/api/v1/*" zone_name = "ratesapi.nz" [[routes]] pattern = "ratesapi.nz/openapi" zone_name = "ratesapi.nz" [[routes]] pattern = "ratesapi.nz/openapi/*" zone_name = "ratesapi.nz" ``` The file has the same `/openapi` routes for `www.ratesapi.nz`. ### Apex Redirect `apps/web/src/server.ts` sends a `301` redirect from each apex request to the same path on `www.ratesapi.nz`. The API routes on the apex domain do not get this redirect, because the API Worker answers them first. Thus the API clients that use `https://ratesapi.nz` continue to operate. The landing page Worker also sends the old docs paths (`/api-reference/*`, `/open-source/*`, and `/llms.txt`) to the same paths under `/docs`. ## Build Build each app in the same way as the CI build job: ```bash bun run build ``` The API build is a Wrangler dry run. The docs build makes the OpenNext Worker bundle. The web build uses the Cloudflare Vite plugin to make the landing page Worker bundle. ## Deploy Deploy the three Workers: ```bash bun run deploy ``` To deploy one Worker only, use the name of the app: ```bash bun run --filter api deploy bun run --filter docs deploy bun run --filter web deploy ``` ## GitHub Actions | Workflow | Starts | Secrets | | ------------------------------------ | ---------------------------------------------- | -------------------------------------------------- | | Deploy to Cloudflare | On each push to `main`, or manually | `CLOUDFLARE_API_TOKEN` | | Hourly Scraping and Database Updates | Each hour, on each push to `main`, or manually | `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` | Each workflow runs `bun run check` before it deploys or collects data. ## After Deployment * Make sure that `https://www.ratesapi.nz/` shows the landing page. * Make sure that `https://www.ratesapi.nz/docs` shows the docs app. * Make sure that `https://ratesapi.nz/` gives a `301` redirect to `https://www.ratesapi.nz/`. * Make sure that `https://www.ratesapi.nz/api/v1/health` and `https://ratesapi.nz/api/v1/health` give a response from the API Worker. * Make sure that `https://www.ratesapi.nz/openapi` shows the Scalar API client. * Make sure that `https://www.ratesapi.nz/openapi/json` gives the OpenAPI document. * Make sure that the repository has the secrets that the workflows use. ## Production Rules * Keep the route control clear: the landing page Worker controls the Custom Domains, the docs Worker controls `/docs`, and the API Worker controls the API paths. * When you change Worker bindings or route patterns, examine the CI build job. * Keep the OpenAPI descriptions and schemas aligned with the routes. * Monitor scraper failures separately from Worker uptime. --- # Monitoring URL: https://www.ratesapi.nz/docs/open-source/monitoring How GitHub Actions check Rates API uptime every 15 minutes, monitor the hourly collection of New Zealand rates, and open issues for failures. Rates API uses GitHub Actions workflows to monitor the service. The workflows monitor uptime, data collection, and code quality. ## Uptime Checks The `API Uptime Monitoring` workflow starts every 15 minutes. It does these steps: 1. It installs the dependencies with Bun. 2. It runs `apps/api/bin/uptime-check.ts`. This script sends requests to the production API at `https://www.ratesapi.nz/api/`. 3. If one or more requests fail, the workflow fails. 4. If the workflow fails, it makes a GitHub issue with the failure details. If the API returns HTTP 500, 502, 503, or 504, the script sends the request again. It does this a maximum of 3 times. ## Monitored Endpoints * The health endpoint * The list endpoint and one detail endpoint for each category * The time-series endpoint for each category, with no filter and with an institution or issuer filter * The mortgage time series, with a date, with a date range, and with the `termInMonths` filter The script does not check `/openapi` or `/openapi/json`. ## Alerts When a request fails, the workflow makes a GitHub issue with the title `🚨 API Endpoint(s) Down`. The issue has the labels `incident` and `high-priority`. It mentions the owner of the repository, and it contains the output of the failed requests. ## Necessary Permissions ```yaml permissions: issues: write contents: read ``` If your organization limits permissions, make sure that GitHub Actions can make issues. Also make sure that the `incident` and `high-priority` labels exist. ## Data Collection The `Hourly Scraping and Database Updates` workflow starts each hour. It also starts on each push to `main`. It does these steps: 1. It runs `bun run check`. 2. It makes sure that it can get access to the remote D1 database. 3. It runs the four scraper scripts. 4. It shows the saved data in the workflow log. A scraper saves a dataset only when the data changed. ## Code Quality These workflows start for pull requests and for pushes to `main`: | Workflow | Checks | | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `CI` | `bun run check` (Oxlint, Oxfmt, TypeScript through `tsgo`, docs type generation, and tests) and `bun run build` (the API dry run, the docs OpenNext build, and the web build) | | `Fallow` | Static analysis of the code | | `React Doctor` | React diagnostics for the changed files | GitHub code scanning also runs CodeQL analysis through its default setup. This analysis does not have a workflow file in the repository. The `Deploy to Cloudflare` workflow starts on each push to `main`. It runs `bun run check` before it deploys. ## Add Checks * To monitor a new endpoint, add it to `apps/api/bin/uptime-check.ts`. * If a docs route becomes important, add a check for it. * If GitHub issues are not enough, add a notification channel. * If old data is more important than endpoint uptime, add a limit for data age. --- # About, Data Source, and Terms URL: https://www.ratesapi.nz/docs/about Who runs Rates API, where the New Zealand rate data comes from, its limits, fair use, the licences of the code and the data, and how to get help. ## Who Operates Rates API Simon Betton, an independent software developer, makes and operates Rates API. For more information about him, go to [simonbetton.com](https://www.simonbetton.com). Rates API is an open source project. The source code, the issues, and the history of changes are in the [GitHub repository](https://github.com/simonbetton/ratesapi.nz). Rates API is independent. It is not affiliated with interest.co.nz or with a lender. interest.co.nz and the lenders do not endorse Rates API. ## How Data Collection Operates 1. Each hour, a GitHub Actions workflow runs the scraper scripts. 2. The scripts read the rate pages of [interest.co.nz](https://www.interest.co.nz) for mortgages, personal loans, car loans, and credit cards. 3. If the data of a dataset changed, the API saves the new data. The API does not save data that did not change. 4. The API keeps a maximum of one snapshot for each dataset on each UTC date. The history starts on 8 March 2025. Rates API does not get data directly from lenders. To see the time of the last change to each dataset, send a request to [`/api/v1/health`](https://www.ratesapi.nz/api/v1/health). For more information, read [Core Concepts](https://www.ratesapi.nz/docs/api-reference/concepts). ## Limitations * The data can be incorrect, incomplete, or late. A lender can change a rate before the next check. * If interest.co.nz changes its pages, the data collection can fail. Then the data stays the same until the scripts are repaired. * Before you make a decision, check the rate with the lender. * Rates API does not give financial advice. * Rates API is free and has no service level agreement. The service can be unavailable. ## Fair Use An API key is not necessary, and Rates API has no published rate limit. Obey these rules, so that the service stays available for all users: * Keep the responses in a cache. The API checks for new data only one time each hour. * Do not send many requests in a short time. For example, do not send one request for each visitor to your site. * To get earlier rates, use the time-series endpoints. Do not use the list endpoints to make your own history. Rates API can block traffic that causes problems for the service. ## Data Source and Licence | Item | Licence or terms | | ----------- | ------------------------------------------------------------------------------- | | Source code | The [MIT licence](https://github.com/simonbetton/ratesapi.nz/blob/main/LICENSE) | | Rate data | The [terms of interest.co.nz](https://www.interest.co.nz/terms-conditions) | The MIT licence applies only to the source code. It does not apply to the rate data. The rate data comes from interest.co.nz, and the terms of interest.co.nz apply to it. Rates API cannot give you permission to use the data. Before you republish, distribute, or sell the data, read the terms of interest.co.nz. If you show the data in your product, tell your users that the data comes from interest.co.nz. ## Frequently Asked Questions ### Is Rates API affiliated with interest.co.nz? No. Rates API is an independent project. It is not affiliated with interest.co.nz, and interest.co.nz does not endorse it. Rates API reads the rate pages of interest.co.nz each hour. ### Can I use the data commercially? The source code has the MIT licence. Thus you can use and change the code in commercial work. The rate data is different. It comes from interest.co.nz, and the terms of interest.co.nz apply to it. Before you use the data in a commercial product, read the [terms of interest.co.nz](https://www.interest.co.nz/terms-conditions). If the terms do not permit your use, ask interest.co.nz for permission. ## Contact To report an incorrect rate or a problem, ask a question, or suggest a change, [open an issue on GitHub](https://github.com/simonbetton/ratesapi.nz/issues).