This is the documentation and tutorial on REST API that enables access to open data published by CZ.NIC. This API is used in CZ.NIC’s statistical dashboards.
A recommended pre-requisite is the curl command-line tool that will be used in examples below.
Data about CZ registry, DNS traffic, MojeID and other services operated by CZ.NIC are regularly retrieved from various sources and stored in the service database PostgreSQL. See ADAM home page for more information about data acquisition.
The REST API is then generated automatically on top of the service database using the PostgREST API server. This powerful tool exposes all database tables and views as REST API endpoints. This means that a database query with all its parameters is encoded as an Uniform Resource Locator (URL). That’s why the syntax of PostgREST query parameters is somewhat cryptic. After all, a REST API is primarily intended for machine access, especially for use in web application. However, as we will try to demonstrate below, with some practice it can be used by mere mortals as well.
The base URL of CZ.NIC’s statistical open data is https://stats.nic.cz. The set of endpoints generated by PostgREST is flat, meaning that all URL routes are just one level deep. So all URLs are of this general form:
https://stats.nic.cz/<endpoint>?<parameters>
where <parameters>
(together with the question
mark) are optional.
PostgREST also generates a machine-readable specification of the REST API using the OpenAPI language. For human users, a more useful interface to the OpenAPI document is the Swagger UI. This web interface provides a list od all available endpoints and corresponding query parameters:
In order to make the set of endpoints (currently with almost 150 items) more manageable, the endpoints are split into ten categories described in the following table:
Category | Contents |
---|---|
cz | DNS traffic stats for .cz domain |
ca | DNS traffic stats for .ca domain |
crawler | data obtained from DNS crawler |
dns | parameters of DNS service (round-trip time) |
domain | ranking of second-level domains under .cz |
fred | CZ registry data |
fredlog | statistics of CZ registry operations |
internal | internal endpoints (not intended for public use) |
mojeid | statistics of MojeID identity service |
odvr | DNS traffic stats for ODVR public resolver |
Access to endpoints that are labelled with a padlock icon is restricted to authenticated and authorized users. In order to work with such endpoints in the Swagger web interface, one can either log in to the dashboards in another window/tab, or click on the padlock icon and enter an API token.
Imagine that we need to research recent trends in the Czech domain market. For this we need the numbers of domains registered in the CZ registry by individual registrars over the period since January 1, 2022, let’s say. How can we obtain this data?
First, the table in the previous section indicates that registry data reside in the fred category (the name relates to the fact that FRED is the software that CZ.NIC developed and uses for operating the CZ registry). In this category, the endpoint /fred_domains_by_registrar looks particularly promising.1
Clicking on the endpoint’s name in the Swagger web interface leads to an expanded view that displays all query parameters available for the endpoint, as shown in this (partial) screenshot:
The query parameters will be explained in detail below. Some of them are related to the individual fields (database columns) of the resource represented by the endpoint, while others are generic and serve specific purposes in the PostgREST API.
For now, let’s concentrate on another useful feature of the Swagger UI, namely the ability to execute API queries. After clicking the Try it out button, the query parameter values can be filled in the endpoint’s form and the query fired by pressing the Execute button below. It can be very helpful for getting an overall idea about the contents of the endpoint.
But before you really try it and inadvertently freeze your browser window, it is good to learn about the limit query parameter, as the web browser environment isn’t suitable for computationally expensive tasks such as receiving and displaying vast amounts of data. It is therefore wise to set the limit parameter (located in the bottom part of the web form) to a reasonably small number, for example 10.
After executing a query, the browser displays returned data and HTTP response headers, but also shows the query URL constructed from the web form, as well as the complete curl command that can be used on the command line.
As we already said, one part of query parameters is bound to the concrete endpoint (or the underlying database table or view). They can be used for controlling the extent of returned data (records and fields).
Using a special syntax, query parameters can specify conditions on field/column values. Records/rows that do not satisfy the conditions are suppressed in the output. Most frequently used are the following relational operators:
Operator | Meaning |
---|---|
eq |
equal to |
neq |
not equal to |
gt |
greater than |
gte |
greater than or equal to |
lt |
less than |
lte |
less than or equal to |
For example, we might want to query our favourite endpoint
/fred_domains_by_registrar, but only for data related
to the cz domain (otherwise we’d also get data for the ENUM domain
0.2.4.e164.arpa
). To achieve this, we can use the
eq
operator as follows:
$ curl -s "https://stats.nic.cz:443/fred_domains_by_registrar?zone=eq.cz&limit=10"
[{"ts":"2020-05-22T12:00:00+00:00","registrar_id":"REG-1API","domains":11771,"zone":"cz"},
{"ts":"2020-05-22T12:00:00+00:00","registrar_id":"REG-ACTIVE24","domains":175663,"zone":"cz"},
{"ts":"2020-05-22T12:00:00+00:00","registrar_id":"REG-AERO-TRIP","domains":2227,"zone":"cz"},
{"ts":"2020-05-22T12:00:00+00:00","registrar_id":"REG-ASCIO","domains":2783,"zone":"cz"},
{"ts":"2020-05-22T12:00:00+00:00","registrar_id":"REG-ASPONE","domains":2091,"zone":"cz"},
{"ts":"2020-05-22T12:00:00+00:00","registrar_id":"REG-BANAN","domains":5433,"zone":"cz"},
{"ts":"2020-05-22T12:00:00+00:00","registrar_id":"REG-CORE","domains":438,"zone":"cz"},
{"ts":"2020-05-22T12:00:00+00:00","registrar_id":"REG-CT","domains":924,"zone":"cz"},
{"ts":"2020-05-22T12:00:00+00:00","registrar_id":"REG-CZNIC","domains":229,"zone":"cz"},
{"ts":"2020-05-22T12:00:00+00:00","registrar_id":"REG-DIAL-TEL","domains":717,"zone":"cz"}]
This is simple: we only want records that have the zone
field equal to cz
. We also used the limit
parameter to keep the output short (but feel free to omit it if you try
this example on the command line). Note also that, as dictated by URL
query string rules, multiple query parameters are separated by the
&
character.
And by the way, any URL that is passed to curl will work pretty much the same in a web browser:
The next task is slightly more complicated: we want the data starting
from January 1, 2022. This can be accomplished by adding another query
parametr restricting the value of the ts (time stamp) column to
be greater than or equal (gte
) to that date:
$ curl -s "https://stats.nic.cz:443/fred_domains_by_registrar?ts=gte.2022-01-01&zone=eq.cz&limit=10"
[{"ts":"2022-01-01T23:59:59+00:00","registrar_id":"REG-1API","domains":20042,"zone":"cz"},
{"ts":"2022-01-01T23:59:59+00:00","registrar_id":"REG-ACTIVE24","domains":186744,"zone":"cz"},
{"ts":"2022-01-01T23:59:59+00:00","registrar_id":"REG-AERO-TRIP","domains":2201,"zone":"cz"},
{"ts":"2022-01-01T23:59:59+00:00","registrar_id":"REG-ASCIO","domains":4876,"zone":"cz"},
{"ts":"2022-01-01T23:59:59+00:00","registrar_id":"REG-ASPONE","domains":1923,"zone":"cz"},
{"ts":"2022-01-01T23:59:59+00:00","registrar_id":"REG-BANAN","domains":4589,"zone":"cz"},
{"ts":"2022-01-01T23:59:59+00:00","registrar_id":"REG-CORE","domains":489,"zone":"cz"},
{"ts":"2022-01-01T23:59:59+00:00","registrar_id":"REG-CT","domains":855,"zone":"cz"},
{"ts":"2022-01-01T23:59:59+00:00","registrar_id":"REG-CZNIC","domains":249,"zone":"cz"},
{"ts":"2022-01-01T23:59:59+00:00","registrar_id":"REG-DIAL-TEL","domains":740,"zone":"cz"}]
PostgREST has a few more operators for specifying conditions on field
values. Moreover, logical operators (and
, or
and not
) can be used to combine conditions in different
ways. See the documentation
to get the complete story.
Orthogonally to filtering records/rows, PostgREST also allows for selecting output fields/columns using the select query parameter.
For example, in the previous section we restricted output records to
those having cz
in the zone field. This means that
this field is no more interesting and can be removed from the
output:
$ curl -s "https://stats.nic.cz:443/fred_domains_by_registrar?select=ts,registrar_id,domains&ts=gte.2022-01-01&zone=eq.cz&limit=10"
[{"ts":"2022-01-01T23:59:59+00:00","registrar_id":"REG-1API","domains":20042},
{"ts":"2022-01-01T23:59:59+00:00","registrar_id":"REG-ACTIVE24","domains":186744},
{"ts":"2022-01-01T23:59:59+00:00","registrar_id":"REG-AERO-TRIP","domains":2201},
{"ts":"2022-01-01T23:59:59+00:00","registrar_id":"REG-ASCIO","domains":4876},
{"ts":"2022-01-01T23:59:59+00:00","registrar_id":"REG-ASPONE","domains":1923},
{"ts":"2022-01-01T23:59:59+00:00","registrar_id":"REG-BANAN","domains":4589},
{"ts":"2022-01-01T23:59:59+00:00","registrar_id":"REG-CORE","domains":489},
{"ts":"2022-01-01T23:59:59+00:00","registrar_id":"REG-CT","domains":855},
{"ts":"2022-01-01T23:59:59+00:00","registrar_id":"REG-CZNIC","domains":249},
{"ts":"2022-01-01T23:59:59+00:00","registrar_id":"REG-DIAL-TEL","domains":740}]
Note that it isn’t possible to say “select all fields except …”, so we have to specify all the remaining fields.
By default, output records are ordered according to internal rules of the PostgreSQL database. A different ordering (possibly involving multiple fields) can be achieved with the order query parameter.
For example, if we want to order the domains by registrars first by
the registrar ID in the ascending order (asc
), and then by
the timestamp in the descending order (desc
), the following
query will do the trick:
$ curl -s "https://stats.nic.cz:443/fred_domains_by_registrar?select=ts,registrar_id,domains&ts=gte.2022-01-01&zone=eq.cz&order=registrar_id.asc,ts.desc&limit=10"
[{"ts":"2025-04-29T23:59:59+00:00","registrar_id":"REG-1API","domains":22230},
{"ts":"2025-04-28T23:59:59+00:00","registrar_id":"REG-1API","domains":22237},
{"ts":"2025-04-27T23:59:59+00:00","registrar_id":"REG-1API","domains":22240},
{"ts":"2025-04-26T23:59:59+00:00","registrar_id":"REG-1API","domains":22244},
{"ts":"2025-04-25T23:59:59+00:00","registrar_id":"REG-1API","domains":22247},
{"ts":"2025-04-24T23:59:59+00:00","registrar_id":"REG-1API","domains":22303},
{"ts":"2025-04-23T23:59:59+00:00","registrar_id":"REG-1API","domains":22309},
{"ts":"2025-04-22T23:59:59+00:00","registrar_id":"REG-1API","domains":22317},
{"ts":"2025-04-21T23:59:59+00:00","registrar_id":"REG-1API","domains":22321},
{"ts":"2025-04-20T23:59:59+00:00","registrar_id":"REG-1API","domains":22330}]
The URL is already getting a bit complicated, right?
PostgREST uses JSON by default for representing the output records. Sometimes comma-separated values are more convenient, e.g. for use in spreadsheet software. The _accept query parameter (note the leading underscore) can be used to change the representation:
$ curl -s "https://stats.nic.cz:443/fred_domains_by_registrar?select=ts,registrar_id,domains&ts=gte.2022-01-01&zone=eq.cz&order=registrar_id.asc,ts.desc&_accept=text/csv&limit=10"
ts,registrar_id,domains
"2025-04-29 23:59:59+00",REG-1API,22230
"2025-04-28 23:59:59+00",REG-1API,22237
"2025-04-27 23:59:59+00",REG-1API,22240
"2025-04-26 23:59:59+00",REG-1API,22244
"2025-04-25 23:59:59+00",REG-1API,22247
"2025-04-24 23:59:59+00",REG-1API,22303
"2025-04-23 23:59:59+00",REG-1API,22309
"2025-04-22 23:59:59+00",REG-1API,22317
"2025-04-21 23:59:59+00",REG-1API,22321
"2025-04-20 23:59:59+00",REG-1API,22330
We already mentioned that some endpoints in the REST API are restricted to authorized users and thus require authentication. For machine access, e.g. from web applications, a convenient authentication method are API tokens, also known as bearer authentication.
An API token is a random string of 36 characters (hexadecimal digits
and hyphens). Sending it inside the Authorization
HTTP
header allows for accessing protected resources that the token was
created for (see below).
With curl, API tokens can be used as follows:
$ curl -s -H "Authorization: Bearer f85db341-5069-4663-a16c-d79abf86feb8" "https://stats.nic.cz/ca_qps_total_1h?limit=10"
[{"ts":"2020-08-03T22:00:00+00:00","qps":2871.3975},
{"ts":"2020-08-03T23:00:00+00:00","qps":2854.339},
{"ts":"2020-08-04T00:00:00+00:00","qps":3084.3193},
{"ts":"2020-08-04T01:00:00+00:00","qps":3247.8638},
{"ts":"2020-08-04T02:00:00+00:00","qps":2934.327},
{"ts":"2020-08-04T03:00:00+00:00","qps":2954.8071},
{"ts":"2020-08-04T04:00:00+00:00","qps":3129.4106},
{"ts":"2020-08-04T05:00:00+00:00","qps":3029.339},
{"ts":"2020-08-04T06:00:00+00:00","qps":2967.8},
{"ts":"2020-08-04T07:00:00+00:00","qps":2882.29}]
Each user with granted access to some protected REST API resources can create, inspect and revoke API tokens after logging in and selecting Manage tokens in the user menu.
In order to create a new API token, the following items have to be specified in the creation form:
After clicking the button Create a new token, the new token should appear in the list at the top of the page:
A token may be revoked by clicking on the Revoke button.
Another endpoint with a similar name – /fred_domains_by_registrar_latest – has only the most recent values but, remember, we need data since the beginning of the year.↩︎