One API call. Country detection. PPP data. Suggested discount. Local currency. Done.
GET YOUR API KEY READ THE DOCS// HIT CALCULATE.
One API call from your backend with the visitor's country. Get the adjusted price, local currency, and discount. Your API key stays on the server.
Pass the adjusted price to your payment processor. Works with Stripe, Paddle, LemonSqueezy, anything.
Customers see fair prices, get charged fair prices. VPN detection prevents discount abuse. International conversions go up.
Call /v1/pricing from your backend with the visitor's country. Your API key never touches the client. Works with any framework — Next.js, Express, Cloudflare Workers, Rails, Django, anything with HTTP.
// app/api/pricing/route.ts
export async function POST(req) {
const { price } = await req.json();
// Get visitor info from headers (Cloudflare, Vercel, etc.)
const country = req.headers.get('CF-IPCountry')
|| req.headers.get('X-Vercel-IP-Country') || 'US';
const ip = req.headers.get('CF-Connecting-IP')
|| req.headers.get('X-Forwarded-For')?.split(',')[0] || '';
const parity = await fetch(
`https://api.getparity.dev/v1/pricing?base_price=${price}&client_country=${country}`,
{ headers: {
'X-API-Key': process.env.PARITY_KEY,
'X-Client-IP': ip,
}}
).then(r => r.json());
return Response.json({
adjustedPrice: parity.pricing.adjustedPrice,
localPrice: parity.pricing.localPrice,
currency: parity.currency,
discount: parity.pricing.discountPercent,
});
}
app.post('/checkout', async (req, res) => {
const ip = req.headers['x-forwarded-for']?.split(',')[0] || req.ip;
// 1. Get PPP-adjusted price
const parity = await fetch(
`https://api.getparity.dev/v1/pricing?base_price=${req.body.price}&client_country=${req.body.country}`,
{ headers: {
'X-API-Key': process.env.PARITY_KEY,
'X-Client-IP': ip,
}}
).then(r => r.json());
// 2. Create Stripe checkout with adjusted price
const session = await stripe.checkout.sessions.create({
line_items: [{
price_data: {
currency: parity.currency.code.toLowerCase(),
unit_amount: Math.round(parity.pricing.localPrice * 100),
product_data: { name: 'Your Product' },
},
quantity: 1,
}],
mode: 'payment',
});
res.json({ url: session.url });
});
export default {
async fetch(request, env) {
const country = request.cf?.country || 'US';
const ip = request.headers.get('CF-Connecting-IP') || '';
const parity = await fetch(
`https://api.getparity.dev/v1/pricing?base_price=99&client_country=${country}&client_timezone=${request.cf?.timezone}`,
{ headers: {
'X-API-Key': env.PARITY_KEY,
'X-Client-IP': ip,
}}
).then(r => r.json());
return Response.json(parity);
}
};
Pass client_timezone and client_asn for VPN detection. The X-Client-IP header ensures distinct-IP tracking uses the visitor's real IP, not your server's.
Base URL: https://api.getparity.dev/v1
All authenticated endpoints require the X-API-Key header. Hit GET /v1 for machine-readable docs.
Calculate PPP-adjusted pricing. Supports a single price or multiple prices in one call (ideal for shops with many products).
| PARAM | TYPE | REQ | DESCRIPTION |
|---|---|---|---|
| base_price | number | * | Single product price in USD. Use this OR prices. |
| prices | string | * | Comma-separated prices in USD (max 100). Use this OR base_price. |
| country | string | NO | ISO 3166 country code (auto-detected from IP if omitted) |
| token | string | NO | Signed visitor token from /v1/detect (enables server-side calls with accurate VPN detection) |
| vpn_override | string | NO | Set to "true" to flag this request as VPN-detected. Use with your own VPN detection service (e.g. IPinfo). |
| preset | string | NO | Preset ID or name for custom discount rules. Uses active preset or defaults if omitted. |
| client_country | string | NO | Visitor's country code for server-side flow. Overrides auto-detection. |
| client_timezone | string | NO | Visitor's IANA timezone (e.g. Asia/Kolkata) for server-side VPN detection. |
| client_asn | number | NO | Visitor's ASN for server-side VPN detection. |
Header: X-Client-IP — Visitor's real IP for distinct-IP tracking in server-side integrations.
// Single price
curl "https://api.getparity.dev/v1/pricing?base_price=99&country=IN" \
-H "X-API-Key: parity_live_YOUR_KEY"
// Multiple prices (one API call for your entire catalog)
curl "https://api.getparity.dev/v1/pricing?prices=9,29,49&country=IN" \
-H "X-API-Key: parity_live_YOUR_KEY"
{
"country": { "code": "IN", "name": "India", "incomeLevel": "Lower middle income" },
"currency": { "code": "INR", "name": "Indian Rupee", "symbol": "₹" },
"ppp": { "priceLevelRatio": 0.255, "dataYear": 2022, "source": "World Bank ICP" },
"pricing": {
"originalPrice": 99,
"discountPercent": 70,
"adjustedPrice": 29.70,
"localPrice": 2468.06,
"localCurrency": "INR"
},
"discount": { "percent": 70, "source": "default" },
"exchange": { "rate": 83.1, "updatedAt": "2025-01-01T00:00:00.000Z" },
"confidence": { "vpnDetected": false, "vpnConfidence": "none", "vpnSignals": [], "dataFresh": true }
}
// GET /v1/pricing?base_price=99&country=IN&preset=aggressive
{
...
"pricing": {
"originalPrice": 99,
"discountPercent": 65,
"adjustedPrice": 34.65,
"localPrice": 2879.42,
"localCurrency": "INR"
},
"discount": { "percent": 65, "source": "custom", "preset": "aggressive" },
...
}
{
"country": { "code": "IN", "name": "India", "incomeLevel": "Lower middle income" },
"currency": { "code": "INR", "name": "Indian Rupee", "symbol": "₹" },
"ppp": { "priceLevelRatio": 0.255, "dataYear": 2022, "source": "World Bank ICP" },
"pricing": [
{ "originalPrice": 9, "discountPercent": 70, "adjustedPrice": 2.70, "localPrice": 224.42, "localCurrency": "INR" },
{ "originalPrice": 29, "discountPercent": 70, "adjustedPrice": 8.70, "localPrice": 723.14, "localCurrency": "INR" },
{ "originalPrice": 49, "discountPercent": 70, "adjustedPrice": 14.70, "localPrice": 1221.86, "localCurrency": "INR" }
],
"discount": { "percent": 70, "source": "default" },
"exchange": { "rate": 83.1, "updatedAt": "2025-01-01T00:00:00.000Z" },
"confidence": { "vpnDetected": false, "vpnConfidence": "none", "vpnSignals": [], "dataFresh": true }
}
Get a signed visitor token. Call this from the visitor's browser, then pass the token to /v1/pricing from your server. This gives you server-side API key security with accurate client-side VPN detection.
// Client-side (visitor's browser)
const res = await fetch("https://api.getparity.dev/v1/detect");
const { token } = await res.json();
// Server-side (your backend, API key stays secret)
const pricing = await fetch(
`https://api.getparity.dev/v1/pricing?base_price=99&token=${token}`,
{ headers: { "X-API-Key": "parity_live_YOUR_KEY" } }
);
{
"token": "eyJjb3VudHJ5IjoiSU4i...",
"country": "IN",
"expires_in": 300
}
List all supported countries with currency metadata.
curl "https://api.getparity.dev/v1/countries"
Create a free account and receive an API key.
| BODY | TYPE | REQ | DESCRIPTION |
|---|---|---|---|
| string | YES | Your email address |
curl -X POST "https://api.getparity.dev/v1/signup" \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com"}'
{
"apiKey": "parity_live_abc123...",
"plan": "free"
}
Check your current month's request count and limits.
curl "https://api.getparity.dev/v1/usage" \
-H "X-API-Key: parity_live_YOUR_KEY"
{
"plan": "free",
"used": 42,
"limit": 1000,
"period": "2025-01"
}
Create a Stripe checkout session to upgrade to a paid plan.
| BODY | TYPE | REQ | DESCRIPTION |
|---|---|---|---|
| plan | string | YES | "starter" or "pro" |
curl -X POST "https://api.getparity.dev/v1/checkout" \
-H "X-API-Key: parity_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"plan": "starter"}'
Health check. Returns API status and version.
curl "https://api.getparity.dev/v1/health"
Create a named discount preset with per-country rules. Starter: 5 presets, Pro: 20 presets. Not available on Free plan.
| BODY | TYPE | REQ | DESCRIPTION |
|---|---|---|---|
| name | string | YES | Preset name (max 64 chars, unique per account) |
| rules | object | YES | Country code to discount % map. E.g. {"IN": 65, "BR": 45} |
curl -X POST "https://api.getparity.dev/v1/presets" \
-H "X-API-Key: parity_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "aggressive", "rules": {"IN": 65, "BR": 45, "NG": 80}}'
{
"id": "abc123",
"name": "aggressive",
"isActive": false,
"rules": { "IN": 65, "BR": 45, "NG": 80 },
"createdAt": "2026-03-15T00:00:00.000Z",
"updatedAt": "2026-03-15T00:00:00.000Z"
}
List all your discount presets.
curl "https://api.getparity.dev/v1/presets" \
-H "X-API-Key: parity_live_YOUR_KEY"
Get a single preset with its rules.
curl "https://api.getparity.dev/v1/presets/abc123" \
-H "X-API-Key: parity_live_YOUR_KEY"
Update a preset. Full replace of name and rules.
| BODY | TYPE | REQ | DESCRIPTION |
|---|---|---|---|
| name | string | YES | Preset name (max 64 chars, unique per account) |
| rules | object | YES | Country code to discount % map. E.g. {"IN": 65, "BR": 45} |
curl -X PUT "https://api.getparity.dev/v1/presets/abc123" \
-H "X-API-Key: parity_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "conservative", "rules": {"IN": 40, "BR": 30}}'
Delete a preset and its rules. If the preset was active, discounts revert to defaults.
curl -X DELETE "https://api.getparity.dev/v1/presets/abc123" \
-H "X-API-Key: parity_live_YOUR_KEY"
Set a preset as active. All future pricing calls will use its discount rules automatically. Countries not in the preset fall back to PPP defaults.
curl -X POST "https://api.getparity.dev/v1/presets/abc123/activate" \
-H "X-API-Key: parity_live_YOUR_KEY"
Deactivate all presets, reverting to default PPP-based discounts.
curl -X POST "https://api.getparity.dev/v1/presets/deactivate" \
-H "X-API-Key: parity_live_YOUR_KEY"
All errors return { "error": "message", "code": "ERROR_CODE" }
| CODE | MEANING |
|---|---|
| MISSING_PARAM | A required parameter is missing |
| INVALID_PARAM | A parameter value is invalid |
| MISSING_API_KEY | X-API-Key header not provided |
| INVALID_API_KEY | API key is not valid |
| RATE_LIMIT_EXCEEDED | Monthly request limit reached |
| IP_LIMIT_EXCEEDED | Too many distinct IPs for this API key |
| INVALID_TOKEN | Visitor token is invalid or expired |
| COUNTRY_NOT_FOUND | No PPP data for the requested country |
| PRESET_NOT_FOUND | Preset ID or name not found |
| PLAN_LIMIT | Feature not available on current plan |
| PRESET_LIMIT_EXCEEDED | Maximum presets reached for plan |
Every pricing response includes a confidence object with VPN detection signals:
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| vpnDetected | boolean | Whether any VPN signal was detected |
| vpnConfidence | string | "high", "medium", "low", "none", or "external" |
| vpnSignals | array | Signals triggered: "asn_match", "timezone_mismatch", "external_override" |
| dataFresh | boolean | Whether exchange rate data is current |
Need more accurate VPN detection? Use your own provider (e.g. IPinfo, ipgeolocation.io) and pass the result to ParityAPI via the vpn_override parameter.
// Your server — check VPN with your preferred provider
const ipinfo = await fetch(`https://ipinfo.io/${visitorIp}?token=YOUR_IPINFO_TOKEN`);
const { privacy } = await ipinfo.json();
// Pass the result to ParityAPI
const parity = await fetch(
`https://api.getparity.dev/v1/pricing?base_price=99&token=${token}&vpn_override=${privacy.vpn}`,
{ headers: { "X-API-Key": process.env.PARITY_KEY } }
);
// Response will have vpnConfidence: "external" and vpnSignals: ["external_override"]
ALL CORE FEATURES INCLUDED ON EVERY PLAN. CUSTOM PRESETS ON STARTER+. NO CREDIT CARD FOR FREE TIER.