Official XRates SDKs for PHP, JavaScript, Python, Go and Ruby
We've published official client libraries for the five languages that ~90% of our traffic hits the API from: PHP, JavaScript / TypeScript, Python, Go and Ruby. Each one is a thin wrapper over the public REST endpoints — no magic, no codegen, no leaky abstractions. You can read the entire source of any of them in one sitting.
Same surface across all five languages, mapped to whatever your runtime considers idiomatic.
Why thin wrappers instead of generated clients
We considered generating clients from the OpenAPI spec (which we publish anyway — feel free to point any generator at it). We didn't, for one reason: generated clients leak. Names like LatestRequest, LatestResponse, RatesResponseRatesValue are the kind of API surface that haunts your codebase for years because the types are deeply nested and exported from the SDK, not your own domain.
Hand-written wrappers stay small. The PHP client is 218 lines. The Go client is under 200. The JS one is 180. You can read and audit the whole thing in five minutes — important when the SDK sits in the hot path of a billing system or a price calculator.
What's in every SDK
The same seven methods, mapped to whatever the language prefers:
| Method | What it does | Endpoint |
|---|---|---|
latest |
current rates for a base currency | GET /api/v1/latest |
historical |
rates on a specific date | GET /api/v1/{YYYY-MM-DD} |
convert |
one-shot conversion at the latest (or historical) rate | GET /api/v1/convert |
timeseries |
daily rates between two dates | GET /api/v1/timeseries |
fluctuation |
min / max / change between two dates | GET /api/v1/fluctuation |
currencies |
list of supported currencies | GET /api/v1/currencies |
status |
per-source health, mirror of /status | GET /api/v1/status |
And the same four-tier error hierarchy, so you can catch / errors.As on the cases you care about:
ApiError— base. Network failures, malformed responses, unrecognised statusesAuthenticationError— HTTP 401 / 403RateLimitError— HTTP 429ValidationError— HTTP 422, with the raw response body exposed aspayloadso you can show it to users
If you only catch (ApiError), you'll handle everything. If you want to retry with backoff on 429 specifically, branch on RateLimitError. The hierarchy is the same shape in every language.
PHP
composer require xratesapi/php-sdk
use XRatesApi\Client;
$client = new Client(getenv('XRATES_API_KEY'));
$rates = $client->latest('USD', ['EUR', 'GBP']);
$converted = $client->convert('USD', 'EUR', 100);
PSR-18 compatible: pass any GuzzleHttp\ClientInterface to the constructor to wire in retries, proxies or your own logging middleware. PHP 8.1+, requires guzzlehttp/guzzle ^7.5.
JavaScript / TypeScript
npm install @xratesapi/sdk
import { Client } from '@xratesapi/sdk';
const client = new Client(process.env.XRATES_API_KEY!);
const latest = await client.latest({ base: 'USD', symbols: ['EUR', 'GBP'] });
const conversion = await client.convert('USD', 'EUR', 100);
ESM-only, zero runtime dependencies, native fetch (Node 18+, Deno, Bun, modern browsers). Full TypeScript types. Pass your own fetch via options.fetch if you need polyfilling or a mocked test transport.
Python
pip install xratesapi
from xratesapi import Client
with Client("YOUR_API_KEY") as client:
rates = client.latest(base="USD", symbols=["EUR", "GBP"])
converted = client.convert("USD", "EUR", 100)
Built on httpx, supports Python 3.9+. The client is a context manager so the underlying connection pool gets cleaned up automatically. Pass your own httpx.Client if you need transport-level retries, proxies, or async (an async variant ships in a later release).
Go
go get github.com/xratesapi/go-sdk
package main
import (
"context"
"fmt"
"os"
xratesapi "github.com/xratesapi/go-sdk"
)
func main() {
client, _ := xratesapi.NewClient(os.Getenv("XRATES_API_KEY"), nil)
rates, err := client.Latest(context.Background(), xratesapi.RateParams{
Base: "USD",
Symbols: []string{"EUR", "GBP"},
})
if err != nil {
panic(err)
}
fmt.Println(rates)
}
Idiomatic Go: context.Context on every call so you control deadlines and cancellation per-request, errors.As for branching on typed errors, zero third-party dependencies (just stdlib net/http). Plug in your own HTTPClient interface for retries.
Ruby
gem install xratesapi
require "xratesapi"
client = XRatesApi::Client.new(ENV["XRATES_API_KEY"])
rates = client.latest(base: "USD", symbols: %w[EUR GBP])
converted = client.convert("USD", "EUR", 100)
Ruby 2.7+ (3.x recommended), zero third-party dependencies — just stdlib Net::HTTP and JSON. Drops into a Rails initializer in three lines and doesn't pull in Faraday, HTTParty or anything that fights with whatever you already have in your Gemfile.
Error handling, side by side
The same conceptual code, five languages:
PHP
try {
$client->latest('XXX');
} catch (ValidationException $e) {
print_r($e->payload());
} catch (RateLimitException $e) {
// back off and retry
} catch (AuthenticationException $e) {
// refresh / log in
} catch (ApiException $e) {
// anything else
}
TypeScript
try {
await client.latest({ base: 'XXX' });
} catch (err) {
if (err instanceof ValidationError) console.error(err.payload);
else if (err instanceof RateLimitError) /* retry */;
else if (err instanceof AuthenticationError) /* re-auth */;
else if (err instanceof ApiError) console.error(err.status);
}
Python
try:
client.latest(base="XXX")
except ValidationError as e:
print(e.payload)
except RateLimitError:
pass # retry
except AuthenticationError:
pass # re-auth
except ApiError as e:
print(e.status)
Go
_, err := client.Latest(ctx, xratesapi.RateParams{Base: "XXX"})
var ve *xratesapi.ValidationError
var rl *xratesapi.RateLimitError
var auth *xratesapi.AuthenticationError
var api *xratesapi.APIError
switch {
case errors.As(err, &ve): fmt.Println(ve.Payload)
case errors.As(err, &rl): // retry
case errors.As(err, &auth): // re-auth
case errors.As(err, &api): fmt.Println(api.Status)
}
Ruby
begin
client.latest(base: "XXX")
rescue XRatesApi::ValidationError => e
p e.payload
rescue XRatesApi::RateLimitError
# retry
rescue XRatesApi::AuthenticationError
# re-auth
rescue XRatesApi::ApiError => e
warn e.status
end
This is intentional. Switching languages on the team — or running the same logic in two places (a Node webhook handler and a Python cron job, say) — should not mean re-learning the SDK.
What's next
We're tracking adoption to decide where the sixth SDK goes. The top three candidates are Java / Kotlin (Spring Boot, Android), Rust (for the people emailing us about cargo packages), and C# / .NET (NuGet, Azure Functions). If you want to vote with your stack, drop us a line at [email protected].
In the meantime: all five current SDKs are MIT-licensed, ~200 lines of source each, live under github.com/xratesapi, and ship issues + PRs to the language-specific repo. Pin a version, read the source, ship.
If you haven't yet, grab an API key — the free tier covers 1,000 requests a day, no card required, and works against every method documented above.