Skip to content

Migrating from another financial data provider

This guide is the migration path from one specific shape of financial data API that uses comma-separated batches, camelCase JSON, ISO 8601 timestamps, and interval strings like 1day and 1month. We deliberately mirror that shape: in many cases, only the host name needs to change.

The 30-second version

ConcernWhat changesWhat stays the same
Hostapi.oneapi.finance instead of the previous host.Path prefix (/v1/...).
AuthAuthorization: Bearer oa_live_... header.Bearer-token model.
EndpointsPaths and query parameters are identical for the headline endpoints.Batched ?symbols= (max 8 here).
Response keyscamelCase (previousClose, changePercent, marketCap).Same JSON shape.
Rate limitsDifferent plan caps, different burst behavior.Retry-After and 429.
Error envelopeStandardized envelope: { code, message, status, details }.HTTP status conventions.
New fieldsmeta.source, meta.fetched_at.Existing fields retained.
Removed fieldsReal-time prices (we are 15-min delayed). Some niche statistics fields.All commonly used core fields.

What is the same

For the headline endpoints, the request and response shapes match field-for-field:

  • GET /v1/quote?symbol=AAPL — same request, same response object shape.
  • GET /v1/time_series?symbol=AAPL&interval=1day&outputsize=30 — same response, including descending-time order.
  • GET /v1/statistics?symbol=AAPL — same field names (marketCap, trailingPe, etc).
  • GET /v1/profile?symbol=AAPL — same fields.
  • GET /v1/dividends?symbol=KO — same fields, batched the same way.
  • GET /v1/symbol_search?query=apple — same fields.

If your application deserializes the previous vendor’s responses, swap the host and the auth header and the deserializer keeps working.

What is different

1. Real-time vs delayed

Our quotes are 15-minute delayed. The previous vendor offered real-time on paid plans. If you are switching specifically because of price, you may also need to architect around the delay. Read the delayed-data concept in full.

2. Source attribution

Every response carries a meta.source field naming the upstream that produced the row, plus meta.fetched_at. The previous vendor did not surface this. You can ignore it (your old deserializer will see it as an unknown field), or use it for monitoring and freshness checks. See source attribution.

{
"quote": {
"symbol": "AAPL",
"price": 175.43,
"meta": {
"source": "yahoo_query1",
"fetched_at": "2026-05-04T20:14:32Z"
}
}
}

3. Plan limits

TierOld vendor’s freeOld vendor’s paid (entry)oneapi.finance freeoneapi.finance Indie
Monthly cap800 calls/day (~24,000/mo)varied1,000 / month100,000 / month
Per-minute cap8 / min12-55 / min8 / min60 / min
Cost$0$79+ / mo€0€19 / mo

The economics are designed for indie developers and side-projects rather than small institutional desks. If you need >Pro-tier scale, talk to us.

4. Rate-limit headers

We expose a richer set of rate-limit headers, including separate per-minute and per-month counters:

Old vendoroneapi.finance
api-credits-leftX-RateLimit-Remaining-Minute and X-RateLimit-Remaining-Month
api-credits-used-day(not exposed; use X-RateLimit-Remaining-Month)
api-credits-used-monthX-RateLimit-Limit-Month - X-RateLimit-Remaining-Month
(none)X-RateLimit-Reset-Minute and X-RateLimit-Reset-Month

See rate limits for the full table.

5. Error envelope

We return a standardized error envelope. The previous vendor used a flat string in some cases and a nested meta object in others. Our shape is uniform:

{
"code": "rate_limit",
"message": "Per-minute rate limit exceeded.",
"status": 429,
"details": { "scope": "minute", "retry_after_seconds": 4 }
}

The code value is what your application should branch on. See errors for the full code list.

6. Batch size

Batched requests are capped at 8 symbols per call (the previous vendor allowed up to 120). This is because the cache hit rate degrades sharply past ~8 symbols and most clients are better served by parallelism. If you have batches of >8, group into chunks of 8 client-side; one batched call is one token regardless of count.

7. Symbology

We use the same Yahoo-style suffix scheme (BMW.DE, 0700.HK, RIO.AX). Your symbol map should not need changes. If you previously used the no-suffix format with an exchange= query param, that also still works.

A drop-in adapter

If you want to keep your client code unchanged for now, here is a minimal adapter that shims the host and adds the bearer header:

import os, httpx
class OneApiClient:
def __init__(self, api_key: str | None = None, base: str = "https://api.oneapi.finance"):
self.base = base
self.headers = {"Authorization": f"Bearer {api_key or os.environ['ONEAPI_KEY']}"}
def get(self, path: str, **params):
# Maps the previous vendor's path conventions verbatim.
r = httpx.get(f"{self.base}{path}", params=params, headers=self.headers, timeout=15.0)
if not r.is_success:
raise httpx.HTTPStatusError(r.text, request=r.request, response=r)
return r.json()
# Drop-in:
client = OneApiClient()
quote = client.get("/v1/quote", symbol="AAPL")
class OneApiClient {
constructor(apiKey = process.env.ONEAPI_KEY, base = "https://api.oneapi.finance") {
this.base = base;
this.headers = { Authorization: `Bearer ${apiKey}` };
}
async get(path, params = {}) {
const url = `${this.base}${path}?${new URLSearchParams(params)}`;
const r = await fetch(url, { headers: this.headers });
if (!r.ok) throw new Error(`HTTP ${r.status}: ${await r.text()}`);
return r.json();
}
}

Migration checklist

  1. Sign up at oneapi.finance/signup and mint a key.
  2. Set ONEAPI_KEY in every environment.
  3. Run an A/B comparison: for the next 1,000 requests, fire each request to both providers and log the diff. Most fields should match within rounding; prices will differ slightly because of the delay window.
  4. Add meta.source to your monitoring as a quality signal.
  5. Update your retry logic to match our code-based error envelope.
  6. Cap batched requests at 8 symbols.
  7. Flip the flag.
  8. Cancel the old subscription.

See also