Quickstart — statistical data¶
This walkthrough mirrors the tuikr README: start from the theme catalog, drill
down to a table, and pull the observations behind it. Every code block is runnable
as-is; output shapes are described rather than pasted, because TUIK's catalog
changes between releases and a stale printout would mislead you.
1. List the themes¶
Columns: theme_name, theme_id. This is the same catalog the portal's landing
page shows, and the first call also performs the cookie session dance the API
requires — subsequent calls reuse the session.
Turkish labels come from the same tree:
2. Look at one theme's resources¶
Columns: theme_name, theme_id, resource_name, resource_type,
dataflow_id, resource_url.
resource_type is one of five kinds, taken from the portal's own icon field:
| Type | Meaning |
|---|---|
dataflow |
An SDMX dataflow you can pull with get_data |
istab |
A static file download (XLS/CSV) |
database |
A link into the legacy biruni interactive database |
press |
A press release |
report |
A published report |
Filter at the source when you only want some of them:
Bad theme IDs are helpful, not cryptic
Passing an unknown theme raises TuikValidationError listing every valid
id = name pair — the behavior tuikr users rely on.
3. Narrow to tables and databases¶
Two convenience filters wrap get_resources:
tables = pytuik.get_tables(theme=101) # dataflow + istab
dbs = pytuik.get_databases(theme=101) # legacy database links
get_tables columns: theme_name, theme_id, table_name, node_type,
dataflow_id, table_url.
get_databases columns: theme_name, theme_id, db_name, db_url.
Rows whose node_type is dataflow carry a dataflow_id such as
TR,DF_ADNKS_T26,1.0 — agency, flow, version. That triple is the key to the SDMX
service; istab rows have <NA> there and are downloaded from table_url
directly.
4. Fetch the observations¶
The SDMX service is currently gated
As of 2026-08-10 nsiws.tuik.gov.tr answers every anonymous request with
401 Unauthorized, so the calls in this section raise TuikSDMXError. See
known limitations. Everything below describes
the contract pytuik implements and is unchanged by that outage.
You get a tidy long frame: the surviving dimensions first, each optionally
followed by its {DIM}_label column, then obsTime and obsValue.
Three things happen to the raw SDMX response, ported verbatim from tuikr:
- every string cell is whitespace-trimmed;
- invariant dimensions are dropped — a dimension with one distinct value carries no information in this slice, so the column goes;
- for each surviving coded dimension, a
{DIM}_labelcolumn is inserted right after it — but only when the labels actually differ from the codes.
obsValue is a float; every other column uses pandas' string dtype.
The obsTime/obsValue names are kept verbatim (not snake_cased) to match both
tuikr and the wider SDMX ecosystem.
Turn off what you do not need¶
# Skip the second HTTP call that fetches labels — halves requests for bulk pulls.
pytuik.get_data("TR,DF_ADNKS_T26,1.0", labels=False)
# Keep every dimension, even the constant ones.
pytuik.get_data("TR,DF_ADNKS_T26,1.0", drop_invariant=False)
# Turkish labels.
pytuik.get_data("TR,DF_ADNKS_T26,1.0", lang="tr")
5. When key="ALL" is rejected¶
Some dataflows are too large for ALL, or reject it outright. You then need a
narrower SDMX key — a dot-separated path with one slot per dimension, in key
order. Ask the structure for that order:
structure = pytuik.get_data_structure("TR,DF_ADNKS_T26,1.0")
structure.dimensions # ['REF_AREA', 'SEX', ...] — key order
structure.time_dimension # 'TIME_PERIOD'
structure.label_maps["SEX"] # {'T': 'Total', 'M': 'Male', ...}
Build the key by filling the slots you want to constrain and leaving the rest empty:
# one slot per non-time dimension: constrain the first, leave the rest open
slots = ["" for _ in structure.dimensions]
slots[0] = "TR"
key = ".".join(slots) # e.g. 'TR..' for three dimensions
pytuik.get_data("TR,DF_ADNKS_T26,1.0", key=key)
structure.dimensions lists the non-time dimensions only, so its length is
exactly the number of slots the key needs.
This is a deliberate improvement over tuikr
tuikr keeps the structure internal, which leaves users guessing at keys.
get_data_structure is public here for exactly this reason.
6. Reuse one client¶
The module-level functions delegate to a lazily created default client. For scripts that make many calls — or that need different timeouts, retries, or a Turkish default — construct your own:
from pytuik import TuikClient
with TuikClient(lang="tr", timeout=60.0, retries=5, cache_ttl=3600) as client:
themes = client.get_themes()
df = client.get_data("TR,DF_ADNKS_T26,1.0", start="2020")
Every module-level function exists as a method with the same signature. To reconfigure the process-wide default instead:
The theme tree and the geographic catalog are cached in memory for cache_ttl
seconds (one hour by default; cache_ttl=0 disables it, client.clear_cache()
empties it).
7. Handle failures¶
from pytuik import (
TuikError, # base class for everything below
TuikHTTPError, # non-2xx after retries; carries status + URL
TuikPortalError, # the portal envelope came back isError=True
TuikSDMXError, # SDMX service or parse failure (e.g. key rejected)
TuikValidationError, # bad arguments — raised before any network I/O
)
try:
df = pytuik.get_data("TR,DF_SOMETHING,1.0")
except TuikSDMXError as exc:
print("upstream said no:", exc)
TuikValidationError also subclasses ValueError, so callers that never imported
pytuik's exceptions still catch it naturally.
8. Be a good citizen¶
TUIK publishes no rate limit and no API contract. Requests are made one at a time
with a browser-like User-Agent; keep it that way — no parallel hammering, and
add a courtesy delay in bulk loops: