Developers
Free public API for holidays, HTTP status codes, colors and CPF/CNPJ
arielton.com runs a free, read-only JSON API at https://www.arielton.com/api/v1 with public holidays for Brazil, the United States and Portugal, every HTTP status code, the CSS and Tailwind CSS v4 colors, and CPF/CNPJ check-digit validation. There is no key and no signup, and CORS is open to any origin, so you can call it straight from the browser.
Quick start
Every endpoint is a plain HTTPS GET that returns JSON. Try one in a terminal:
curl https://www.arielton.com/api/v1/holidays/brazil/2026| Base URL | https://www.arielton.com/api/v1 |
|---|---|
| Authentication | None, no key or signup |
| Format | JSON, UTF-8, pretty-printed |
| CORS | Access-Control-Allow-Origin: * on every response |
| Caching | Reference data: public, max-age=86400. CPF/CNPJ: no-store |
| Versioning | Path prefix /v1. Breaking changes would ship as /v2 |
| Index | GET /api/v1 lists every endpoint |
Endpoints
| Method | Path | Returns |
|---|---|---|
| GET | /api/v1/holidays/{country}/{year} | Every national, federal and optional holiday of one country in one year |
| GET | /api/v1/http-status | Every code with its title, class and one-line meaning |
| GET | /api/v1/http-status/{code} | One code with retry, caching and spec details |
| GET | /api/v1/colors | All 148 CSS named colors and 286 Tailwind CSS v4 colors with hex values |
| GET | /api/v1/colors/{name} | One color in hex, RGB, HSL and OKLCH with WCAG contrast |
| GET | /api/v1/cpf/validate/{cpf} | Check-digit verdict for one CPF |
| POST | /api/v1/cpf/validate | Same, with the number in the JSON body |
| GET | /api/v1/cnpj/validate/{cnpj} | Check-digit verdict for one CNPJ, numeric or alphanumeric |
| POST | /api/v1/cnpj/validate | Same, with the number in the JSON body |
Every response also has source (the page on arielton.com the data comes from), docs, attribution and, for reference data, updated.
Public holidays
- GET /api/v1/holidays/{country}/{year}
Holidays for Brazil, the United States and Portugal in 2026, 2027 and 2028, computed from the legal rules rather than scraped: Lei 662/1949, Lei 9.093/1995, Lei 6.802/1980, Lei 14.759/2023 and the Portaria MGI 11.460/2025 for Brazil; 5 U.S.C. 6103 and the OPM schedules for the US; articles 234 and 235 of the Código do Trabalho for Portugal.
Brazilian pontos facultativos (Carnival, Corpus Christi, Ash Wednesday until 2 pm) and the Portuguese feriado facultativo (Carnival) come back with type "optional". Filter them out when you need days off that apply to everyone. State and municipal holidays are not included.
Parameters
| Name | In | Description |
|---|---|---|
country | path | brazil, united-states or portugal, or the lowercase ISO 3166-1 code: br, us, pt. |
year | path | 2026, 2027 or 2028. |
Examples
curl https://www.arielton.com/api/v1/holidays/brazil/2026const res = await fetch('https://www.arielton.com/api/v1/holidays/br/2027');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const { holidays } = await res.json();
const daysOff = holidays
.filter((h) => h.type !== 'optional')
.map((h) => h.observed);import requests
res = requests.get("https://www.arielton.com/api/v1/holidays/united-states/2027", timeout=10)
res.raise_for_status()
for h in res.json()["holidays"]:
print(h["observed"], h["name"]["en"])Response fields
| Field | Type | Description |
|---|---|---|
country | object | slug, ISO code and name in English and Portuguese. |
count | number | Number of items in holidays. |
holidays[].id | string | Stable identifier, e.g. tiradentes or juneteenth. |
holidays[].date | string | Legal date, YYYY-MM-DD. |
holidays[].observed | string | The day off. Differs from date only for US federal holidays on a weekend: Saturday moves to Friday, Sunday to Monday. New Year's Day on a Saturday is observed on December 31 of the previous year. |
holidays[].weekday | string | Weekday of date, lowercase English (monday). |
holidays[].type | string | national (Brazil, Portugal), federal (US) or optional. |
holidays[].name | object | Name in English (en) and Portuguese (pt). |
holidays[].officialName | string | Name in the country's official language, as the law writes it. |
holidays[].legalBasis | string | Law or ordinance that creates the day. |
holidays[].rule | object | How the date is set, in words (fixed, days after Easter, third Monday). |
holidays[].movable | boolean | true for days tied to Easter. |
holidays[].partialDay | object | null | Part-day optional days, e.g. Ash Wednesday until 2 pm. |
holidays[].federalStaffOnly | boolean | true for Dia do Servidor Público (federal civil servants only). |
Sample response
{
"country": {
"slug": "brazil",
"code": "BR",
"name": {
"en": "Brazil",
"pt": "Brasil"
}
},
"year": 2026,
"count": 19,
"holidays": [
{
"id": "confraternizacao",
"date": "2026-01-01",
"observed": "2026-01-01",
"weekday": "thursday",
"type": "national",
"name": {
"en": "New Year's Day",
"pt": "Confraternização Universal"
},
"officialName": "Confraternização Universal",
"legalBasis": "Lei 662/1949",
"rule": {
"en": "Fixed: January 1",
"pt": "Fixo: 1º de janeiro"
},
"movable": false,
"partialDay": null,
"federalStaffOnly": false
},
{
"id": "carnaval-segunda",
"date": "2026-02-16",
"observed": "2026-02-16",
"weekday": "monday",
"type": "optional",
"name": {
"en": "Carnival Monday",
"pt": "Carnaval (segunda-feira)"
},
"officialName": "Carnaval",
"legalBasis": "Portaria MGI",
"rule": {
"en": "48 days before Easter",
"pt": "48 dias antes da Páscoa"
},
"movable": true,
"partialDay": null,
"federalStaffOnly": false
}
],
"updated": "2026-09-27",
"source": "https://www.arielton.com/holidays/brazil/2026",
"docs": "https://www.arielton.com/developers#holidays",
"attribution": "Data by Arielton Oberek (arielton.com). Free to use; please link to the source URL when you publish it."
}HTTP status codes
- GET /api/v1/http-status
- GET /api/v1/http-status/{code}
The registered codes from the IANA HTTP Status Code Registry and RFC 9110, plus the unofficial ones that show up in real logs: nginx 444 and 499 and the Cloudflare 520 to 526 range, flagged with official: false.
Each item carries a one-line meaning in English and Portuguese that you can show next to an error, and a link to the section of the RFC that defines it.
Parameters
| Name | In | Description |
|---|---|---|
code | path | Three-digit status code, e.g. 404. The list endpoint shows every code available. |
Examples
curl https://www.arielton.com/api/v1/http-status/429const res = await fetch('https://www.arielton.com/api/v1/http-status/503');
const status = await res.json();
console.log(`${status.code} ${status.title}: ${status.meaning.en}`);import requests
codes = requests.get("https://www.arielton.com/api/v1/http-status", timeout=10).json()["statuses"]
server_errors = [s["code"] for s in codes if s["class"] == "server"]
print(server_errors)Response fields
| Field | Type | Description |
|---|---|---|
code | number | The status code. |
title | string | Registered reason phrase (Not Found). |
titlePt | string | Portuguese gloss of the title (Não encontrado). |
class | string | informational, success, redirection, client, server or unofficial. classInfo adds the range (4xx) and a readable name. |
meaning | object | One-line meaning in en and pt. |
official | boolean | false for nginx and Cloudflare codes; unofficialBy says which. |
retry | object | Whether a client may retry, in a short phrase. |
cacheable | string | heuristic (cacheable by default, RFC 9110 section 15.1), explicit (only with Cache-Control or Expires), never, or validator (304). |
spec | object | label and url of the defining document. |
deprecated | object | null | Set for reserved or obsolete codes. |
Sample response
{
"code": 429,
"title": "Too Many Requests",
"titlePt": "Pedidos demais",
"class": "client",
"meaning": {
"en": "Rate limited: too many requests in a time window. Wait, then retry.",
"pt": "Limite de taxa atingido: pedidos demais num intervalo. Espere e tente de novo."
},
"official": true,
"source": "https://www.arielton.com/http-status/429",
"classInfo": {
"key": "client",
"range": "4xx",
"name": {
"en": "Client errors",
"pt": "Erros do cliente"
}
},
"alsoKnownAs": [],
"unofficialBy": null,
"deprecated": null,
"retry": {
"en": "Yes, after the delay in Retry-After, or with exponential backoff and jitter if there is none",
"pt": "Sim, depois do tempo indicado em Retry-After, ou com espera exponencial e jitter se ele não vier"
},
"cacheable": "never",
"spec": {
"label": "RFC 6585 §4",
"url": "https://www.rfc-editor.org/rfc/rfc6585#section-4"
},
"updated": "2026-09-27",
"docs": "https://www.arielton.com/developers#http-status",
"attribution": "Data by Arielton Oberek (arielton.com). Free to use; please link to the source URL when you publish it."
}Colors
- GET /api/v1/colors
- GET /api/v1/colors/{name}
The 148 named colors of CSS Color Module Level 4 and the Tailwind CSS v4 default palette (tailwindcss 4.3.3). Tailwind v4 defines its colors in OKLCH; tailwind.token is the exact theme value, and hex is the sRGB value after CSS Color 4 gamut mapping when the color falls outside sRGB (inSrgbGamut: false).
Contrast ratios follow WCAG 2.x and are floored to two decimals, so a ratio of 4.499 reports 4.49 and fails AA instead of rounding up to a pass.
Parameters
| Name | In | Description |
|---|---|---|
name | path | A CSS keyword in lowercase (rebeccapurple) or a Tailwind color as hue-shade (blue-500). |
Examples
curl https://www.arielton.com/api/v1/colors/rebeccapurpleconst res = await fetch('https://www.arielton.com/api/v1/colors/blue-500');
const color = await res.json();
button.style.background = color.css.oklch;
button.style.color = color.contrast.bestText;import requests
c = requests.get("https://www.arielton.com/api/v1/colors/teal", timeout=10).json()
print(c["hex"], c["contrast"]["white"]["ratio"], c["contrast"]["white"]["aa"])Response fields
| Field | Type | Description |
|---|---|---|
kind | string | css or tailwind. |
hex | string | Lowercase #rrggbb. |
rgb / hsl / oklch | object | Numeric channels. oklch.l runs from 0 to 1. |
css | object | Ready-to-paste CSS strings for hex, rgb(), hsl() and oklch(). |
contrast | object | Ratio against white and black with AA/AAA pass flags for normal and large text, and bestText: the text color with more contrast. |
aliases / family | string[] / string | CSS colors only: same-value keywords (gray, grey) and hue family. |
tailwind | object | Tailwind colors only: hue, shade, token, inSrgbGamut. |
Sample response
{
"name": "rebeccapurple",
"kind": "css",
"hex": "#663399",
"rgb": {
"r": 102,
"g": 51,
"b": 153
},
"hsl": {
"h": 270,
"s": 50,
"l": 40
},
"oklch": {
"l": 0.4403,
"c": 0.1603,
"h": 303.37
},
"css": {
"hex": "#663399",
"rgb": "rgb(102, 51, 153)",
"hsl": "hsl(270, 50%, 40%)",
"oklch": "oklch(44% 0.16 303.4)"
},
"contrast": {
"white": {
"ratio": 8.4,
"aa": true,
"aaLarge": true,
"aaa": true,
"aaaLarge": true
},
"black": {
"ratio": 2.49,
"aa": false,
"aaLarge": false,
"aaa": false,
"aaaLarge": false
},
"bestText": "white"
},
"aliases": [],
"family": "purple",
"source": "https://www.arielton.com/colors/rebeccapurple",
"updated": "2026-09-27",
"docs": "https://www.arielton.com/developers#colors",
"attribution": "Data by Arielton Oberek (arielton.com). Free to use; please link to the source URL when you publish it."
}CPF validation
- GET /api/v1/cpf/validate/{cpf}
- POST /api/v1/cpf/validate
Checks the two CPF check digits (mod 11) and rejects numbers made of one repeated digit, which pass the math but are never issued. It does not query the Receita Federal: valid means well formed, not registered or active.
Nothing is stored or logged by the handler, and responses are sent with Cache-Control: no-store so no CDN or browser cache keeps them. For real people's numbers, prefer POST: a number in the URL path can end up in proxy access logs and browser history.
Parameters
| Name | In | Description |
|---|---|---|
cpf | path | 11 digits; dots, dash and spaces are ignored. |
cpf | body | JSON body {"cpf": "529.982.247-25"}, up to 1 KB. |
Examples
curl -X POST https://www.arielton.com/api/v1/cpf/validate \
-H 'Content-Type: application/json' \
-d '{"cpf": "529.982.247-25"}'const res = await fetch('https://www.arielton.com/api/v1/cpf/validate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ cpf: input.value })
});
const { valid, formatted, message } = await res.json();import requests
r = requests.get("https://www.arielton.com/api/v1/cpf/validate/52998224725", timeout=10).json()
print(r["valid"], r["formatted"], r["reason"])Response fields
| Field | Type | Description |
|---|---|---|
valid | boolean | true when both check digits match and the digits are not all the same. |
formatted | string | null | Masked as 000.000.000-00 when there are 11 digits. |
reason | string | valid, empty, invalidChars, invalidLength, repeated, firstDigit, secondDigit, bothDigits or tooLong. |
message | object | The reason as a sentence, in en and pt. |
fiscalRegion | object | null | The 9th digit and the states of the fiscal region that issued the number. |
Sample response
{
"valid": true,
"formatted": "529.982.247-25",
"reason": "valid",
"message": {
"en": "Both check digits match. This confirms the number is well formed, not that it is registered with the Receita Federal.",
"pt": "Os dois dígitos verificadores conferem. Isso mostra que o número é bem formado, não que esteja cadastrado na Receita Federal."
},
"fiscalRegion": {
"digit": 7,
"states": [
"ES",
"RJ"
]
},
"source": "https://www.arielton.com/tools/cpf-validator",
"docs": "https://www.arielton.com/developers#cpf",
"attribution": "Data by Arielton Oberek (arielton.com). Free to use; please link to the source URL when you publish it."
}CNPJ validation
- GET /api/v1/cnpj/validate/{cnpj}
- POST /api/v1/cnpj/validate
Validates both CNPJ formats: the classic 14 digits and the alphanumeric CNPJ the Receita Federal issues from July 2026, where the first 12 characters may be letters A to Z. Each character is valued by its ASCII code minus 48, so the numeric algorithm is the special case of the new one.
The formatted CNPJ contains a slash, which cannot sit inside a URL path segment. Send the characters without the mask on GET, or use POST.
Parameters
| Name | In | Description |
|---|---|---|
cnpj | path | 14 characters without the slash, e.g. 11222333000181 or 12ABC34501DE35. |
cnpj | body | JSON body {"cnpj": "12.ABC.345/01DE-35"}, mask allowed, up to 1 KB. |
Examples
curl https://www.arielton.com/api/v1/cnpj/validate/11222333000181const res = await fetch('https://www.arielton.com/api/v1/cnpj/validate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ cnpj: '12.ABC.345/01DE-35' })
});
const { valid, format } = await res.json();import requests
r = requests.post("https://www.arielton.com/api/v1/cnpj/validate", json={"cnpj": "12.ABC.345/01DE-35"}, timeout=10)
print(r.json()["valid"], r.json()["format"])Response fields
| Field | Type | Description |
|---|---|---|
valid | boolean | true when both check digits match. |
formatted | string | null | Masked as 00.000.000/0000-00 when there are 14 characters. |
reason / message | string / object | Same values as the CPF endpoint. |
format | string | null | numeric or alphanumeric. |
branch / headquarters | string / boolean | The 4 branch characters; 0001 is the headquarters (matriz). |
Sample response
{
"valid": true,
"formatted": "12.ABC.345/01DE-35",
"reason": "valid",
"message": {
"en": "Both check digits match. This confirms the number is well formed, not that the company exists or is active.",
"pt": "Os dois dígitos verificadores conferem. Isso mostra que o número é bem formado, não que a empresa exista ou esteja ativa."
},
"format": "alphanumeric",
"branch": "01DE",
"headquarters": false,
"source": "https://www.arielton.com/tools/cnpj-validator",
"docs": "https://www.arielton.com/developers#cnpj",
"attribution": "Data by Arielton Oberek (arielton.com). Free to use; please link to the source URL when you publish it."
}Errors
Unknown values in the path (a country, year, status code or color the API does not have) return HTTP 404. The reference endpoints are prerendered, so check res.ok before parsing the body.
A POST without a usable JSON body returns 400 with {"error": {"status": 400, "message": "..."}}. An invalid CPF or CNPJ is not an error: it returns 200 with valid: false and a reason.
Fair use
- No key and no fixed quota. Be reasonable: this runs on a personal site.
- Cache responses. Reference data changes only when a page is reviewed, and the CDN already keeps it for 24 hours.
- Use GET /api/v1/http-status and GET /api/v1/colors to fetch everything in one request instead of looping over single items.
- Send a User-Agent that identifies your app, so problems can be reported to you instead of blocked.
- The service is free and best effort, with no uptime guarantee. If your product depends on it, copy the data you need; the lists are small.
Attribution
Attribution is requested, not required. If you publish data from the API, link to the source URL of the response, for example:
<p>Holiday data: <a href="https://www.arielton.com/holidays/brazil/2026">arielton.com</a></p>Privacy of CPF and CNPJ checks
- The number is used to compute the answer and then discarded. The handler does not store it, log it or send it anywhere.
- Responses carry Cache-Control: private, no-store, so neither the CDN nor the browser keeps a copy.
- Like any web request, the URL may appear in the hosting platform's standard access logs. POST keeps the number out of the URL.
Embeddable widgets
No code at all: the holiday countdown, the holiday list and the HTTP status card are available as iframes you can paste into any page.
Need a custom API or integration?
The same approach (static data, prerendered JSON, no database to run) works for price tables, catalogs, internal reference data and public datasets. Arielton builds APIs and integrations in TypeScript and Go.
Email contact@arielton.com or use the contact page /contact.
Changelog
- v1 released: holidays, HTTP status codes, colors, CPF and CNPJ validation.
- Embeddable widgets: next holiday countdown, holiday list, HTTP status card.
Frequently asked questions
- Do I need an API key?
- No. There is no key, no signup and no account. Send a plain GET (or POST for CPF/CNPJ) and read the JSON.
- Is there a rate limit?
- No fixed quota is published. Responses are cached by the CDN for 24 hours, so cache them on your side too and use the list endpoints instead of looping over single items. Traffic that looks abusive may be blocked.
- Can I use the data in a commercial project?
- Yes. The API is free for personal and commercial projects. Attribution is requested, not required: a link to the source URL in each response is what keeps it free.
- Does the CPF endpoint check whether a CPF exists?
- No. It checks the two check digits and the repeated-digit rule, which is everything that can be known from the number itself. Whether a CPF is registered and regular can only be checked with the Receita Federal.
- Can I call the API from the browser?
- Yes. Every response carries Access-Control-Allow-Origin: *, and the POST endpoints answer the CORS preflight, so fetch works from any site without a proxy.
Last reviewed by Arielton Oberek.