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
| Concern | What changes | What stays the same |
|---|---|---|
| Host | api.oneapi.finance instead of the previous host. | Path prefix (/v1/...). |
| Auth | Authorization: Bearer oa_live_... header. | Bearer-token model. |
| Endpoints | Paths and query parameters are identical for the headline endpoints. | Batched ?symbols= (max 8 here). |
| Response keys | camelCase (previousClose, changePercent, marketCap). | Same JSON shape. |
| Rate limits | Different plan caps, different burst behavior. | Retry-After and 429. |
| Error envelope | Standardized envelope: { code, message, status, details }. | HTTP status conventions. |
| New fields | meta.source, meta.fetched_at. | Existing fields retained. |
| Removed fields | Real-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
| Tier | Old vendor’s free | Old vendor’s paid (entry) | oneapi.finance free | oneapi.finance Indie |
|---|---|---|---|---|
| Monthly cap | 800 calls/day (~24,000/mo) | varied | 1,000 / month | 100,000 / month |
| Per-minute cap | 8 / min | 12-55 / min | 8 / min | 60 / 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 vendor | oneapi.finance |
|---|---|
api-credits-left | X-RateLimit-Remaining-Minute and X-RateLimit-Remaining-Month |
api-credits-used-day | (not exposed; use X-RateLimit-Remaining-Month) |
api-credits-used-month | X-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
- Sign up at oneapi.finance/signup and mint a key.
- Set
ONEAPI_KEYin every environment. - 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.
- Add
meta.sourceto your monitoring as a quality signal. - Update your retry logic to match our
code-based error envelope. - Cap batched requests at 8 symbols.
- Flip the flag.
- Cancel the old subscription.