ForexAPI Documentation
Self-hosted currency exchange rate API. 30 currencies, official daily rates from the European Central Bank, historical data back to 1999, and instant conversion — deployed on your own VPS.
Base URL: https://forexapi.aiclipcutter.tech
| Endpoint | Description | Plan |
|---|---|---|
GET /v1/latest | Latest exchange rates | Free |
GET /v1/convert | Convert amount between currencies | Free |
GET /v1/currencies | List all supported currencies | Free |
GET /v1/historical/:date | Rates for a specific past date | Starter+ |
GET /v1/timeseries | Rates over a date range | Pro+ |
GET /v1/fluctuation | % change between two dates | Pro+ |
Quick Start (Local Dev)
Run the full stack locally in under 5 minutes.
bash# 1. Clone the project git clone https://github.com/yourusername/forex-api.git cd forex-api # 2. Install dependencies npm install # 3. Start Redis via Docker docker run -d -p 6379:6379 --name forex-redis redis:7-alpine # 4. Copy .env cp .env.example .env # 5. Start the API (dev mode with auto-reload) npm run dev # → Server running on http://localhost:3000 # 6. Create your first API key npm run create-key admin@example.com business # 7. Test it curl "http://localhost:3000/v1/latest?base=USD&symbols=EUR,GBP" \ -H "X-Api-Key: fxk_your_key_here"
npm run seed. This downloads ~40MB from ECB and takes about 60 seconds. Rates go back to 1999.Authentication
Pass your API key via header (recommended) or query parameter. Both are supported for Fixer.io compatibility.
Header (recommended)curl "https://forexapi.aiclipcutter.tech/v1/latest" \ -H "X-Api-Key: fxk_your_key_here"
Query param (Fixer.io-compatible)curl "https://forexapi.aiclipcutter.tech/v1/latest?access_key=fxk_your_key_here"
On RapidAPI, the platform injects X-RapidAPI-Key automatically — you don't need to manage keys for marketplace users.
GET /v1/latest
Returns the latest exchange rates. The ECB publishes once per business day around 16:00 CET; the cache is refreshed hourly so a new publication is picked up within the hour. Available on all plans.
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
base | string | EUR | Base currency code |
symbols | string | all | Comma-separated currencies to return |
RequestGET /v1/latest?base=USD&symbols=EUR,GBP,JPY,INR
Response{ "success": true, "timestamp": 1725356400000, "base": "USD", "date": "2026-09-03", "rates": { "EUR": 0.9212, "GBP": 0.7874, "JPY": 147.32, "INR": 83.91 } }
GET /v1/convert
Convert a specific amount between two currencies in a single call.
Parameters
| Param | Required | Description |
|---|---|---|
from | Yes | Source currency code |
to | Yes | Target currency code |
amount | Yes | Amount to convert (numeric) |
RequestGET /v1/convert?from=USD&to=EUR&amount=100
Response{ "success": true, "query": { "from": "USD", "to": "EUR", "amount": 100 }, "info": { "rate": 0.9212, "timestamp": 1725356400000 }, "date": "2026-09-03", "result": 92.12 }
GET /v1/historical/:date
Historical rates for any date back to 1999-01-04. Requires Starter plan or higher.
RequestGET /v1/historical/2024-01-15?base=USD&symbols=EUR,GBP
Response{ "success": true, "base": "USD", "date": "2024-01-15", "historical": true, "rates": { "EUR": 0.9185, "GBP": 0.7821 } }
npm run seed after deploy. ECB does not publish rates for weekends and bank holidays — the nearest available weekday's rates are returned.GET /v1/timeseries
Rates for every available day within a date range. Max 365 days per request. Requires Pro plan.
RequestGET /v1/timeseries?start=2024-01-01&end=2024-01-07&base=USD&symbols=EUR
Response{ "success": true, "base": "USD", "start_date": "2024-01-01", "end_date": "2024-01-07", "rates": { "2024-01-02": { "EUR": 0.9201 }, "2024-01-03": { "EUR": 0.9195 }, "2024-01-04": { "EUR": 0.9188 } } }
GET /v1/fluctuation
Percentage change and absolute delta between two dates. Requires Pro plan.
RequestGET /v1/fluctuation?start=2024-01-01&end=2024-03-01&base=USD&symbols=EUR,GBP
Response{ "success": true, "base": "USD", "start_date": "2024-01-01", "end_date": "2024-03-01", "rates": { "EUR": { "start_rate": 0.9201, "end_rate": 0.9265, "change": 0.0064, "change_pct": 0.6956 }, "GBP": { "start_rate": 0.7850, "end_rate": 0.7921, "change": 0.0071, "change_pct": 0.9045 } } }
GET /v1/currencies
Returns all supported currency codes with their full names. No authentication required.
RequestGET /v1/currencies
Response{ "success": true, "currencies": { "USD": "United States Dollar", "EUR": "Euro", "GBP": "British Pound Sterling", "... } }
Step-by-Step VPS Deployment
A complete walkthrough from a fresh Ubuntu VPS to a live, HTTPS-secured API.
Provision a VPS
Any KVM2-class VPS works: Hostinger ($7/mo), Hetzner CX22 (€4/mo), Vultr ($12/mo), or DigitalOcean Droplet ($12/mo). Minimum specs: 2 vCPU, 4 GB RAM, 40 GB SSD, Ubuntu 22.04 LTS.
Point a domain at your VPS IP
Add an A record in your DNS: forexapi.aiclipcutter.tech → YOUR_VPS_IP. DNS propagation takes 1–30 minutes.
Install Coolify on the VPS
ssh root@YOUR_VPS_IP
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
Takes ~3 minutes. Access Coolify at http://YOUR_VPS_IP:8000 and complete the setup wizard (create your account, no email required).
Push your code to GitHub
cd forex-api
git init
git add .
git commit -m "feat: initial forex api"
git remote add origin https://github.com/YOURUSER/forex-api.git
git push -u origin main
Connect GitHub to Coolify
Coolify dashboard → Sources → Add GitHub App → authorize the repo. This lets Coolify pull and auto-deploy on push.
Create a new resource in Coolify
Coolify → New Resource → Docker Compose → select your repo → branch: main. Coolify detects docker-compose.yml automatically.
Set environment variables
Resource → Environment Variables → add:
NODE_ENV=production
PORT=3000
APP_URL=https://forexapi.aiclipcutter.tech
REDIS_URL=redis://redis:6379
DB_PATH=/data/forex.db
ADMIN_API_KEY=fxk_generate_a_random_value
ADMIN_EMAIL=admin@yourdomain.com
DEMO_API_KEY=fxk_demo_public
AUTO_SEED=true
Generate ADMIN_API_KEY locally with:
node -e "console.log('fxk_'+require('crypto').randomBytes(16).toString('hex'))"
ADMIN_API_KEY is written into the database on every boot, so a business-tier key exists the moment the first deploy finishes — no docker exec needed.Leave RAPIDAPI_PROXY_SECRET blank for now — you'll fill it in after the RapidAPI listing.
Configure the domain in Coolify
Resource → Domains → add https://forexapi.aiclipcutter.tech. Coolify provisions a Let's Encrypt TLS certificate automatically — no Nginx config needed.
Deploy
Click Deploy. Coolify builds the Docker image, starts the stack (api + redis), and runs health checks. Watch the live log stream. First deploy takes ~3 minutes (image build + model-free Node startup).
Confirm the bootstrap finished
The container seeds itself on first boot: your ADMIN_API_KEY is inserted, and the full ECB history downloads in the background. Watch the Coolify log for:
[db] Admin API key bootstrapped from ADMIN_API_KEY
[cron] Initial rate load complete
[seed] Stored 6900+ days of historical rates
The history seed takes 60–90s and runs detached, so the health check passes immediately. Check progress any time:
curl https://forexapi.aiclipcutter.tech/health
historical_days in the response goes from 0 to ~6,900 once seeding completes.
Add more keys as you get customers
docker exec forex-api node scripts/create-key.js customer@example.com pro
See Create API Keys for the full command reference.
Verify the deployment
# Health check
curl https://forexapi.aiclipcutter.tech/health
# Live rates (use your key)
curl "https://forexapi.aiclipcutter.tech/v1/latest?base=USD&symbols=EUR,GBP" \
-H "X-Api-Key: fxk_your_key"
Coolify Auto-Deploy on Git Push
After initial setup, Coolify can deploy automatically every time you push to main.
Resource → General → enable Auto-deploy on push. Coolify registers a webhook on your GitHub repo. Every push to main triggers a new build and zero-downtime restart.
Seed Historical Data
Historical rates are stored in SQLite, not fetched per request. /v1/historical, /v1/timeseries and /v1/fluctuation all read from that table.
historical_rates table is empty, the container downloads the full ECB history in the background. You only need the commands below to force a refresh or to opt out.Check how many days are loaded:
curl https://forexapi.aiclipcutter.tech/health
Response{ "status": "ok", "redis": "connected", "historical_days": 6934, ... }
Force a re-seed (safe to re-run — rows are replaced, not duplicated):
docker exec forex-api node scripts/seed-history.js
Locally:
npm run seed
To skip the automatic seed, set AUTO_SEED=false in your environment variables.
404 DATE_NOT_FOUND — that is not a seeding failure. Data starts at 1999-01-04.Create API Keys
Keys look like fxk_<32 hex chars> and carry a tier that decides both the monthly quota and which endpoints are reachable.
On the server
docker exec forex-api node scripts/create-key.js customer@example.com pro
OutputAPI Key Created: ----------------------------------------- Key: fxk_9f2c41ab7e6d4058bc13a7e250f9d834 Email: customer@example.com Tier: pro -----------------------------------------
Locally
npm run create-key -- customer@example.com pro
Tiers
| Tier | Monthly limit | Endpoints |
|---|---|---|
demo | 5,000 | latest, convert, currencies |
free | 1,000 | latest, convert, currencies |
starter | 50,000 | + historical |
pro | 500,000 | + timeseries, fluctuation |
business | Unlimited | All |
List and revoke
# List every key
docker exec forex-api node scripts/create-key.js --list
# Revoke one (sets active = 0; the key stops working immediately)
docker exec forex-api node scripts/create-key.js --revoke fxk_9f2c41ab7e6d4058bc13a7e250f9d834
ADMIN_API_KEY is re-inserted as business tier on every boot, so revoking it has no lasting effect — remove the environment variable instead.RapidAPI Marketplace Listing Guide
RapidAPI drives significant organic API discovery traffic. Listing here is how you get your first paying users without marketing spend.
Create a provider account
Go to rapidapi.com/provider → sign up → click + Add New API.
Fill in API details
| API Name | Forex Exchange Rates API |
| Category | Finance → Currency Exchange |
| Base URL | https://forexapi.aiclipcutter.tech |
| Tags | forex, exchange-rate, currency, finance, conversion |
Short description:
Real-time & historical currency exchange rates. 30 ECB currencies.
Up to 5× cheaper than Fixer.io. Free tier included.
Import the OpenAPI spec
In RapidAPI: Endpoints tab → Import OpenAPI. Paste this spec URL or upload the JSON file from your repo:
https://forexapi.aiclipcutter.tech/openapi.json
/openapi.json route to Express that serves the spec, or just manually add each endpoint in the RapidAPI UI — it takes ~10 minutes.Add endpoints manually (if not using OpenAPI)
For each endpoint in RapidAPI → Endpoints → Add Endpoint:
| Method | Path | Description |
|---|---|---|
| GET | /v1/latest | Latest rates. Params: base, symbols |
| GET | /v1/convert | Convert. Params: from, to, amount |
| GET | /v1/historical/{date} | Historical. Path param: date (YYYY-MM-DD) |
| GET | /v1/timeseries | Timeseries. Params: start, end, base, symbols |
| GET | /v1/fluctuation | Fluctuation. Params: start, end, base, symbols |
| GET | /v1/currencies | Currency list (no auth) |
Proxy Secret Setup
The proxy secret prevents people from calling your VPS directly, bypassing RapidAPI and their billing.
Get the secret from RapidAPI
RapidAPI dashboard → Your API → Security tab → copy the X-RapidAPI-Proxy-Secret value.
Add it to your Coolify env vars
Coolify → Resource → Environment Variables → add:
RAPIDAPI_PROXY_SECRET=paste_the_value_from_rapidapi_here
That's it — validation is already built in
src/middleware/auth.js reads RAPIDAPI_PROXY_SECRET at request time. When the secret matches, the request is trusted as RapidAPI gateway traffic and its X-RapidAPI-Subscription plan is mapped onto a local tier (see Tier Mapping); RapidAPI does the metering, so no local quota is applied. Requests that arrive without the secret still work if they carry a valid direct API key. For reference, this is the logic that runs:
// Validate RapidAPI proxy secret — blocks direct callers
const proxySecret = req.headers['x-rapidapi-proxy-secret'];
if (process.env.RAPIDAPI_PROXY_SECRET && proxySecret !== process.env.RAPIDAPI_PROXY_SECRET) {
// Not from RapidAPI — check if they have a direct key instead
const directKey = req.headers['x-api-key'] || req.query.access_key;
if (!directKey) {
return res.status(403).json({
success: false,
error: { code: 'DIRECT_ACCESS_DENIED', message: 'Access this API via RapidAPI or a valid API key.' }
});
}
}
Redeploy
Push the change or click Deploy in Coolify. RapidAPI traffic now passes through automatically; direct callers without a key get a 403.
Pricing Plans on RapidAPI
In RapidAPI → Your API → Pricing → Add Plan for each tier:
| Plan Name | Price | Requests/month | Overage |
|---|---|---|---|
| BASIC | $0/mo | 1,000 | No overage |
| STARTER | $4.99/mo | 50,000 | $0.0001/req |
| PRO | $14.99/mo | 500,000 | $0.00003/req |
| ULTRA | $39.99/mo | Unlimited | — |
Tier Mapping
For RapidAPI users, trust RapidAPI's plan enforcement. For direct customers, your middleware handles it.
| RapidAPI Plan | Your Tier | Monthly Limit | Endpoints |
|---|---|---|---|
| BASIC (Free) | free | 1,000/mo | latest, convert, currencies |
| STARTER | starter | 50,000/mo | + historical |
| PRO | pro | 500,000/mo | + timeseries, fluctuation |
| ULTRA | business | Unlimited | All |
Error Codes
| HTTP | Code | Meaning |
|---|---|---|
| 401 | MISSING_KEY | No API key provided |
| 403 | INVALID_KEY | Key not found or revoked |
| 403 | UPGRADE_REQUIRED | Endpoint requires a higher plan |
| 403 | DIRECT_ACCESS_DENIED | Request did not come via RapidAPI |
| 400 | MISSING_PARAM | Required query parameter absent |
| 400 | INVALID_DATE | Date not in YYYY-MM-DD format |
| 400 | INVALID_CURRENCY | Currency code not supported |
| 400 | INVALID_BASE | Base currency not covered by the ECB feed |
| 400 | INVALID_PARAM | Parameter present but not a valid value |
| 400 | INVALID_RANGE | start is later than end |
| 400 | RANGE_TOO_LARGE | Timeseries range exceeds 365 days |
| 404 | DATE_NOT_FOUND | No ECB data for that date (weekend/holiday) |
| 429 | RATE_LIMIT_EXCEEDED | Monthly quota exhausted |
| 500 | INTERNAL_ERROR | Unexpected server error |
| 503 | RATES_UNAVAILABLE | ECB feed unreachable and no cached snapshot exists |
Rate Limit Headers
Every authenticated response includes:
| Header | Value |
|---|---|
X-RateLimit-Limit | Monthly request cap for your plan |
X-RateLimit-Remaining | Requests left this month |
X-RateLimit-Reset | ISO timestamp when the counter resets (1st of next month, UTC) |
Retry-After | Seconds until the quota resets. Sent only on a 429. |
Fixer.io Compatibility
ForexAPI is designed as a drop-in Fixer.io alternative. Change the base URL and you're done for most endpoints:
| Fixer.io | ForexAPI equivalent |
|---|---|
GET /latest?access_key=KEY | GET /v1/latest?access_key=KEY |
GET /convert?from=USD&to=EUR&amount=10 | GET /v1/convert?from=USD&to=EUR&amount=10 |
GET /2024-01-15?access_key=KEY | GET /v1/historical/2024-01-15 |
GET /timeseries?start_date=… | GET /v1/timeseries?start=… |
access_key query param is supported alongside X-Api-Key header, so existing Fixer.io clients often work without code changes.