Rates API · v1
Read Moneybees exchange rates from your server
The Rates API returns the current rates that Moneybees quotes for each asset pair. Each rate has a mid rate, a buy rate and a sell rate. The API is read-only: you cannot place orders or change rates with it.
- MethodGET only
- FormatJSON
- AuthAPI key, scope
rates:read - Limit60 requests per minute (default)
Quick start
-
Create a key
Sign in to the Moneybees partner portal and open API keys. Select Generate key. Copy the secret: the portal shows it only once.
-
Store the key on your server
Put the secret and your base URL in your secret manager or in environment variables. The examples use
MONEYBEES_API_KEYandMONEYBEES_BASE_URL. -
Call the API
Send the key in the
X-API-Keyheader.Shellcurl -H "X-API-Key: $MONEYBEES_API_KEY" \ "$MONEYBEES_BASE_URL/api/v1/rates/USD_PHP"
Base URL
Moneybees gives you two base URLs when you onboard: one for tests and one for live rates. All paths in this guide are relative to the base URL. Every path starts with /api/v1.
Call the API only from your server. Do not call it from a browser or a mobile app, because the key would then be visible to your users.
API keys
Every request must carry an API key. Use one of these headers:
X-API-Key: YOUR_SECRET
Authorization: Bearer YOUR_SECRET
If you send both headers, Moneybees uses X-API-Key. The key needs the rates:read scope. Moneybees sets the scopes on your partner account, so every key on the account has the same scopes.
Manage your keys
The API keys page in the partner portal shows each key, its status, its expiry date and its requests today.
- Generate
- Creates a key that is valid for 90 days. The portal shows the secret only once. You can have at most 2 active keys. During a rotation, the old key also works until its grace time ends.
- Rotate
- Creates a new key to replace an old key. The old key stays valid for 24 hours, or until its own expiry date if that is sooner. Then it stops. Switch your integration to the new key within that time.
- Revoke
- Stops a key at once. You can add a reason. Revoke a key if its secret leaks.
- Expiry
- Each key expires 90 days after Moneybees issues it. After that date, every request with the key returns
401. Rotate the key before it expires.
Keep the key secret
Store the key in a secret manager or an environment variable. Never put it in source code, a shared document, a chat message or an email.
A key that is unknown, revoked or expired gets the same 401 response, so the response does not say which. Every 401 has a WWW-Authenticate header.
List all rates
GET/api/v1/rates
Returns every pair that has a current rate.
curl -H "X-API-Key: $MONEYBEES_API_KEY" \
"$MONEYBEES_BASE_URL/api/v1/rates"
const res = await fetch(`${process.env.MONEYBEES_BASE_URL}/api/v1/rates`, {
headers: { 'X-API-Key': process.env.MONEYBEES_API_KEY },
});
if (!res.ok) {
const err = await res.json();
throw new Error(`${res.status} ${err.code}: ${err.message}`);
}
const { data } = await res.json();
for (const rate of data) {
console.log(rate.assetPair, rate.buyRate, rate.sellRate);
}
import os
from decimal import Decimal
import requests
res = requests.get(
f"{os.environ['MONEYBEES_BASE_URL']}/api/v1/rates",
headers={"X-API-Key": os.environ["MONEYBEES_API_KEY"]},
timeout=10,
)
res.raise_for_status()
for rate in res.json()["data"]:
print(rate["assetPair"], Decimal(rate["buyRate"]), Decimal(rate["sellRate"]))
Response 200 (example values)
{
"data": [
{
"assetPair": "USD_PHP",
"base": "USD",
"quote": "PHP",
"mid": "58.1234567",
"buyRate": "58.41407398349999",
"sellRate": "57.8328394165",
"computedAt": "2026-10-20T04:00:00.000Z"
}
]
}
The list leaves out a pair that has no current rate or no price. So the list can be shorter than the full set of pairs. See Freshness.
Get one rate
GET/api/v1/rates/{pair}
Returns the rate for one pair. Write the pair as BASE_QUOTE, for example USD_PHP. Pair names are not case sensitive: usd_php and USD_PHP are the same pair.
curl -H "X-API-Key: $MONEYBEES_API_KEY" \
"$MONEYBEES_BASE_URL/api/v1/rates/USD_PHP"
const res = await fetch(`${process.env.MONEYBEES_BASE_URL}/api/v1/rates/USD_PHP`, {
headers: { 'X-API-Key': process.env.MONEYBEES_API_KEY },
});
if (res.status === 429) {
const waitSeconds = Number(res.headers.get('Retry-After') ?? '60');
// Wait waitSeconds, then retry.
} else if (!res.ok) {
const err = await res.json();
throw new Error(`${res.status} ${err.code}: ${err.message}`);
} else {
const rate = await res.json();
console.log(rate.buyRate, rate.sellRate, rate.computedAt);
}
import os
from decimal import Decimal
import requests
res = requests.get(
f"{os.environ['MONEYBEES_BASE_URL']}/api/v1/rates/USD_PHP",
headers={"X-API-Key": os.environ["MONEYBEES_API_KEY"]},
timeout=10,
)
if res.status_code == 429:
wait_seconds = int(res.headers.get("Retry-After", "60"))
# Wait wait_seconds, then retry.
else:
res.raise_for_status()
rate = res.json()
print(Decimal(rate["buyRate"]), Decimal(rate["sellRate"]), rate["computedAt"])
Response 200 (example values)
{
"assetPair": "USD_PHP",
"base": "USD",
"quote": "PHP",
"mid": "58.1234567",
"buyRate": "58.41407398349999",
"sellRate": "57.8328394165",
"computedAt": "2026-10-20T04:00:00.000Z"
}
Fields
| Field | Type | Meaning |
|---|---|---|
assetPair | string | The pair in BASE_QUOTE form, for example USD_PHP. |
base | string | The base asset code, for example USD. |
quote | string | The quote asset code, for example PHP. |
mid | string | The mid rate. A decimal string with up to 10 decimal places. |
buyRate | string | The price a customer pays Moneybees for one unit of the base asset. It is the mid increased by the buy margin percentage: mid × (1 + margin). A decimal string. |
sellRate | string | The price Moneybees pays a customer for one unit of the base asset. It is the mid decreased by the sell margin percentage: mid × (1 − margin). A decimal string. |
computedAt | string | The UTC time when Moneybees computed the rate, in ISO 8601 format. |
Use decimal math
mid is exact to 10 decimal places. buyRate and sellRate are computed values. They can have up to about 17 significant digits, and the last digits can be rounding noise. Read every rate with a decimal library, such as decimal.Decimal in Python, then round the buy and sell rates to the precision that your product shows.
The number of decimal places can change from one rate to the next. Do not parse the rates with a fixed format.
Freshness
Moneybees serves only current rates. The API never returns a rate that is paused, out of date or not valid.
- The list endpoint leaves out a pair whose rate is paused, out of date or not valid. It also leaves out a pair that has no buy or sell price.
- The single-pair endpoint returns
404with the codenot_foundfor a pair whose rate is paused, out of date or not valid. The message says so. Retry later. - The single-pair endpoint returns
404with the codenot_foundfor a pair that Moneybees does not publish. The message says so. Use the list endpoint to see the available pairs. - The single-pair endpoint returns
503with the codepricing_unavailablewhen a pair has a current rate but no buy or sell price. Retry later.
Use computedAt to show your users when the rate was computed.
Errors
Every error returns a JSON body with a code and a message. Your code must check the code. The message is for people and can change.
{
"code": "unauthorized",
"message": "Send your API key in the X-API-Key header or as Authorization: Bearer <key>."
}
| Status | Code | Meaning | What to do |
|---|---|---|---|
400 | bad_request | The request is not valid. For example, the pair is not in BASE_QUOTE form. | Fix the request. Do not retry the same request. |
401 | unauthorized | The key is missing, unknown, revoked or expired. | Check the key. If it expired, generate a new key. |
403 | forbidden | The key does not have the rates:read scope. | Contact Moneybees to add the scope to your partner account. |
404 | not_found | The path does not exist, the pair is not published, or its rate is paused, out of date or not valid. The message says which. | Check the path and the pair. If the rate is paused or out of date, retry later. |
413 | payload_too_large | The request body is too large. | Send GET requests without a body. |
415 | unsupported_media_type | The request content type is not supported. | Send GET requests without a body. |
429 | rate_limited | You sent too many requests, or the limit service is busy. | Wait for the number of seconds in the Retry-After header, then retry. |
500 | internal_error | Moneybees had an internal error. | Retry later. If the error continues, contact Moneybees. |
503 | pricing_unavailable | The pair has no buy or sell price now. | Retry later. |
Limits
Each key has its own limit per minute. The limit depends on the tier of your partner account, and every key on the account uses that tier. Ask Moneybees which tier your account has.
| Tier | Requests per minute |
|---|---|
| Default | 60 |
| Premium | 600 |
Moneybees also limits each IP address to 300 requests per minute. This limit applies before Moneybees checks the key, so requests with a wrong key also count.
When you reach a limit, the API returns 429 with a Retry-After header. The header gives the number of seconds to wait. Your integration must wait for that time. Do not retry in a tight loop.
Cache the list
One call to the list endpoint returns every pair. Call it on a schedule, for example every 15 seconds, and serve your users from your own copy. Then you use 4 requests per minute.
OpenAPI file
The OpenAPI 3 file describes both endpoints, the fields, the main errors and the two ways to send the key. You can import it into Postman, Insomnia or any OpenAPI tool.
Download rates-api-v1.openapi.json
- In Postman, select Import, then select the file.
- Set the base URL to your Moneybees base URL.
- In the collection's auth settings, set the
X-API-Keyvalue to your secret.
Changelog
- Published the Rates API v1 guide.