API documentation

Everything is served under /v1, returns JSON (or CSV on request), and requires an API key. The interactive endpoint reference lives at https://api.arclanetechnologies.com/v1/docs.

1 · Get a key

Create an account, verify your email, and generate a key. It's shown once — store it like a password. Free keys get the latest two published years of every company (showcase companies: full history). Paid keys get everything.

2 · Authenticate

Send the key on every request:

Authorization: Bearer atlas_sk_your_key_here

3 · First request — coverage

curl "https://api.arclanetechnologies.com/v1/companies" \
  -H "Authorization: Bearer atlas_sk_..."

Returns every published company with its years, statements and years_on_plan — the years your key can access.

4 · A statement, with lineage

curl "https://api.arclanetechnologies.com/v1/companies/JKH/financials?year=FY2025&statement=income_statement&include=components" \
  -H "Authorization: Bearer atlas_sk_..."

include=components attaches the composition lineage to every field: the printed label, note reference, row index, sign-normalisation flag, and the source report + page each figure came from. Add &format=csv (or send Accept: text/csv) for CSV.

5 · Python

import requests

API = "https://api.arclanetechnologies.com/v1"
KEY = "atlas_sk_..."

r = requests.get(
    f"{API}/companies/JKH/financials",
    params={"year": "FY2025", "statement": "income_statement"},
    headers={"Authorization": f"Bearer {KEY}"},
)
r.raise_for_status()
data = r.json()

for f in data["statements"]["income_statement"]["fields"]:
    print(f["field"], f["consolidated_current"])

6 · Five years of revenue into pandas

The timeseries endpoint is the core institutional use case: one field, every published year, one request.

import pandas as pd
import requests

API = "https://api.arclanetechnologies.com/v1"
KEY = "atlas_sk_..."

r = requests.get(
    f"{API}/companies/JKH/timeseries",
    params={"field": "revenue", "entity": "consolidated", "units": "lkr"},
    headers={"Authorization": f"Bearer {KEY}"},
)
r.raise_for_status()

df = pd.DataFrame(r.json()["series"])
df["year"] = df["year"].str.replace("FY", "").astype(int)
df = df.set_index("year")
print(df)
#        value    unit
# year
# 2021   ...      rupees
# 2022   ...      rupees

units=lkr converts everything to absolute rupees — report units can differ across years, and this saves you checking. Leave it off to get values exactly as reported, with a unit per point.

Errors & rate limits

Errors always have one shape: {"error": {"code": "...", "message": "..."}}. Codes you'll see: unauthorized (401, bad or missing key), plan_required (403, the year is outside your plan), not_found (404, no published data), rate_limited (429).

Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset; a 429 includes Retry-After in seconds. Free: 20 requests/min, 1,000/day. Paid: 120/min, 20,000/day.

Field definitions

GET https://api.arclanetechnologies.com/v1/meta/schema?sector=general returns every normalized field per statement with its flags — subtotal (printed subtotal, recomputed and cross-checked by our validation), bucket (heterogeneous rows may map into it), memo (per-share/off-balance items excluded from sums). No key required.

Full endpoint reference
Interactive OpenAPI docs, generated from the live API.
Open https://api.arclanetechnologies.com/v1/docs →