Reference
Prices & rates
Our published buy and sell rates for gold and silver, plus daily and hourly history. Read-only, and the cheapest way to check we're live.
Three endpoints, all requiring the pricing capability, which every partner gets by default. They are the only partner endpoints that move no money and need no user, so they are the natural first call in an integration.
Sandbox returns real prices. There is no simulated price feed — a
oro_test_ token and a oro_live_ token return identical numbers, differing only in the mode field. Anything you build against sandbox prices will behave the same in production.Live rates
GET
/partner/pricespricingNo parameters. Returns both metals.
curl https://api.oropocket.com/partner/prices \
-H "Authorization: Bearer oro_test_xxx"| Field | Type | Description |
|---|---|---|
| buy | number | What you pay per gram to buy, before GST. This is our published rate, not a raw market feed. |
| sell | number | What a user receives per gram when selling. No GST applies on a sell. |
| gst_percentage | number | Currently 3 for both metals. GST applies to buys only. |
| final_buy_price | number | buy plus GST — the per-gram figure a user actually pays. Use this when showing a price to an end user. |
| last_updated | string | ISO timestamp of when we last fetched rates. See the note on freshness below — it is not the moment the price changed. |
| unit | string | Always "INR per gram". Never per 10 grams. |
Response
{
"success": true,
"request_id": "req_c7e2bb20c4ebfc43041f",
"mode": "sandbox",
"data": {
"prices": {
"gold": {
"buy": 16173.53,
"sell": 15551.76,
"gst_percentage": 3,
"final_buy_price": 16658.74,
"last_updated": "2026-08-19T13:35:00.090Z",
"unit": "INR per gram"
},
"silver": {
"buy": 246.66,
"sell": 231.36,
"gst_percentage": 3,
"final_buy_price": 254.06,
"last_updated": "2026-08-19T13:35:00.090Z",
"unit": "INR per gram"
}
}
}
}A single metal
GET
/partner/prices/:assetpricing| Field | Type | Description | |
|---|---|---|---|
| asset | string | required | Path segment. "gold" or "silver", case-insensitive. Anything else is a 400 INVALID_ASSET. |
The response is still nested under the asset key —
data.prices.gold, not data.prices. It is the same object as above with one key instead of two, so the same parsing code works for both endpoints. It can also be null if we have no rate for that metal, so check before reading into it.curl https://api.oropocket.com/partner/prices/gold \
-H "Authorization: Bearer oro_test_xxx"History
GET
/partner/prices/historypricing| Field | Type | Description | |
|---|---|---|---|
| asset | string | required | "gold" or "silver". |
| interval | string | optional | "day" (default) or "hour". |
| from | string | optional | YYYY-MM-DD. Defaults to the earliest date the window allows. |
| to | string | optional | YYYY-MM-DD, inclusive. Defaults to today. Must not be earlier than from. |
| Interval | How far back |
|---|---|
| day | 365 days of daily rates. |
| hour | 7 days of hourly rates. Ask for more and you get 7, not an error — see below. |
Too-wide windows are clamped, not rejected. Ask for 90 days of hourly data and you get the last 7, with
clamped: true and a clamp_reason explaining why. That field is absent entirely when nothing was clamped, so test for clamped rather than for the reason string.| Field | Type | Description |
|---|---|---|
| asset_type | string | Echoes the asset you asked for. |
| interval | string | "day" or "hour", after defaulting. |
| from / to | string | The window actually served, YYYY-MM-DD, after any clamping. |
| max_window_days | number | 365 for day, 7 for hour. |
| clamped | boolean | True when your requested window was wider than retention allows. |
| clamp_reason | string | Only present when clamped is true. |
| count | number | Number of points in series. |
| series[].t | string | A bare "2026-08-19" for interval=day, and a full ISO timestamp for interval=hour. Deliberate — a daily row has no time-of-day to report. |
| series[].buy_price | number | Buy rate at that point, GST-exclusive — comparable to buy, not to final_buy_price. |
| series[].sell_price | number | Sell rate at that point. |
| series[].change_buy | number | Absolute ₹ move from the previous point. 0 rather than null when unknown. |
| series[].change_sell | number | Same, for the sell rate. |
| series[].change_buy_pct | number | The same move as a percentage. |
| series[].change_sell_pct | number | Same, for the sell rate. |
curl "https://api.oropocket.com/partner/prices/history?asset=gold&interval=day&from=2026-08-01&to=2026-08-07" \
-H "Authorization: Bearer oro_test_xxx"Response
{
"success": true,
"request_id": "req_9f11c0b3a7e2d4508cc1",
"mode": "live",
"data": {
"asset_type": "gold",
"interval": "day",
"from": "2026-08-01",
"to": "2026-08-07",
"max_window_days": 365,
"clamped": false,
"count": 2,
"series": [
{ "t": "2026-08-01", "buy_price": 15820.11, "sell_price": 15210.40,
"change_buy": 0, "change_sell": 0, "change_buy_pct": 0, "change_sell_pct": 0 },
{ "t": "2026-08-02", "buy_price": 15901.72, "sell_price": 15288.05,
"change_buy": 81.61, "change_sell": 77.65, "change_buy_pct": 0.52, "change_sell_pct": 0.51 }
]
}
}Series is ordered oldest to newest, and the to day is included.
What these numbers include
| Question | Answer |
|---|---|
| Per gram or per 10g? | Always per gram, both metals. |
| Is GST in there? | Not in buy or sell. final_buy_price is buy + 3%, and is what a user pays. Sells attract no GST. |
| What is the spread? | Not a fixed number, so don't hard-code one. The sell side is adjusted depending on our vault position, so the gap between buy and sell moves on its own. |
| Is this a market feed? | No. It is our published rate — it starts from our supplier's rate and includes our margin. Treat it as the price we will trade at, not as spot. |
| Bitcoin? | Not on the partner API. Pricing covers gold and silver only. |
If you use merchant markup, these prices already include it. Your buy spread is added to
buy (and therefore to gst and final_buy_price), and your sell spread is taken off sell — so what you read here is what you can safely show your users, and it lines up with the rate_inr_per_gram you will get back from /partner/buy/quote and /partner/sell/quote. Our own base rate is never returned here; it is what we settle and pay commission on, and you will see it in your monthly statement.Markup is inert on live until we enable it on your account, so a percentage you set today changes sandbox immediately and live only after approval. Sandbox applies it straight away, which is the point — wire it up and watch it work first. Set it in your dashboard.
Freshness and polling
Rates are refreshed roughly every 30 minutes, and last_updated records when we fetched them — not when the price last moved. Every caller inside that window sees the same value, so polling faster than that gains you nothing but rate-limit consumption.
Sandbox is capped at 1000 requests per day across all endpoints, resetting at 00:00 IST. A price poller is the easiest way to burn that without noticing — at one call every 30 seconds you would exhaust a day's budget before lunch. Cache the response for the life of the quote instead, and see Errors & rate limits.
Errors
| Code | When |
|---|---|
| INVALID_ASSET (400) | The asset was neither gold nor silver. |
| INVALID_INTERVAL (400) | History interval was not day or hour. |
| INVALID_FROM / INVALID_TO (400) | A date that isn't YYYY-MM-DD. |
| INVALID_RANGE (400) | from is later than to. |
| API_NOT_ALLOWED (403) | Your profile lacks the pricing capability. |
| SANDBOX_DAILY_LIMIT (429) | Sandbox daily budget spent. Resets 00:00 IST; live keys are unaffected. |
| PRICES_UNAVAILABLE (503) | We could not reach our rate source and had no usable cached copy. Retry shortly rather than treating it as fatal. |