---
title: "URL parser: parse, canonicalize and extract the registrable domain (eTLD+1)"
method: GET
path: "/v1/code/url"
tags: ["deterministic", "no-api-key", "code", "url", "domains", "psl"]
---

# URL parser: parse, canonicalize and extract the registrable domain (eTLD+1)

`GET /v1/code/url`

## Request

- Base URL: `https://api.duoleads.com`
- URL: `https://api.duoleads.com/v1/code/url`
- Auth: none declared

URL parser: parse and canonicalise a URL, and extract its registrable domain using the Public Suffix List — the part an agent needs to decide whether two URLs belong to the same owner. Returns scheme, host, port, path, decoded query parameters and fragment, the public suffix, the registrable domain (eTLD+1), the subdomain, whether the suffix is ICANN or private, plus a canonical form with default port, dot-segments and percent-encoding normalised. unicode_host uses WHATWG/UTS 46 (non-strict).

**Output (`data`):** canonical, scheme, username (string|null, null without userinfo), host (string|null; null for absolute URLs with no host such as mailto:, urn:, data: and file:///, which parse per WHATWG with path holding everything after the scheme; for those public_suffix, registrable_domain, subdomain, host_type and unicode_host are null and suffix_source is none), port (null when default), path, fragment (string|null, null without a fragment), query [{key, value}], public_suffix (string|null, null for IP hosts), registrable_domain (eTLD+1; string|null, null when public_suffix is null or the host is the suffix itself), subdomain (string|null, null when there is none), suffix_source (icann|private|none), host_type (domain|ipv4|ipv6|idn; null without a host), unicode_host (string|null, null when the host is not an IDN).

Deterministic: identical input returns identical output, so the response is served with `Cache-Control: private, max-age=31536000, immutable` (the paying client may cache it indefinitely; shared caches must not, because the response is paid).

Sources: public-suffix-list a179a48c465e818cfd8d626691cb317985da87fb (published 2026-09-24, MPL-2.0). Echoed in `meta.sources`.

Price: $0.05 USDC per call (x402, Base). Route contract version 1.0.0. Docs: https://api.duoleads.com/docs#code-url

## Query parameters

- `url` string, required
- `base` string

## Response `200`

Paid and settled. `data` is route-specific; a route that judges its input returns 200 with `data.valid: false` for a bad value.

- object
  - `ok` unknown, required
  - `route` '/v1/code/url', required
  - `version` '1.0.0', required
  - `data` object, required: canonical, scheme, username (string|null, null without userinfo), host (string|null; null for absolute URLs with no host such as mailto:, urn:, data: and file:///, which parse per WHATWG with path holding everything after the scheme; for those public_suffix, registrable_domain, subdomain, host_type and unicode_host are null and suffix_source is none), port (null when default), path, fragment (string|null, null without a fragment), query [{key, value}], public_suffix (string|null, null for IP hosts), registrable_domain (eTLD+1; string|null, null when public_suffix is null or the host is the suffix itself), subdomain (string|null, null when there is none), suffix_source (icann|private|none), host_type (domain|ipv4|ipv6|idn; null without a host), unicode_host (string|null, null when the host is not an IDN).
    - `canonical` string, required
    - `scheme` string, required
    - `username` string, nullable, required
    - `host` string, nullable, required
    - `port` string, nullable, required
    - `path` string, required
    - `fragment` string, nullable, required
    - `query` object[], required
      - `key` string, required
      - `value` string, required
    - `public_suffix` string, nullable, required
    - `registrable_domain` string, nullable, required
    - `subdomain` string, nullable, required
    - `suffix_source` string, required
    - `host_type` string, nullable, required
    - `unicode_host` string, nullable, required
  - `meta` Meta, required
    - `disclaimer` string, required: Legal short disclaimer, identical on every response.
    - `request_id` string, required: ULID.
    - `computed_at` string, date-time, required: RFC 3339 UTC with milliseconds.
    - `price_usd` string, required: Decimal string, "0.05".
    - `deterministic` boolean, required
    - `sources` object[]: Present only on routes that read a vendored dataset: the exact edition that answered.
      - `id` string, required
      - `version` string, required
      - `published` string, required
      - `attribution` string
      - `licence` string

## Other responses

- `400`: MISSING_PARAM, INVALID_PARAM or UNSUPPORTED_VALUE. Not charged: validation runs before the paywall.
- `402`: Payment required. No valid PAYMENT-SIGNATURE was supplied: read the base64 PAYMENT-REQUIRED header (or this body) for the price ($0.05 = 50000 atomic USDC on eip155:8453), sign an EIP-3009 authorization and retry. Also returned when settlement is declined (PAYMENT_FAILED). Nothing is charged on a 402.
- `404`: NOT_FOUND: unknown path. Not charged.
- `405`: METHOD_NOT_ALLOWED: only GET and HEAD are served. Not charged.
- `413`: INPUT_TOO_LARGE: a parameter exceeded its documented maximum length. Not charged.
- `500`: INTERNAL: a server bug. Not charged.

---

[API](https://skmtc.dev/duoleads/apis/duo-data-utilities-iban-isin-vat-tax-id-barcode-timezone-geo.md) · [All operations](https://skmtc.dev/duoleads/apis/duo-data-utilities-iban-isin-vat-tax-id-barcode-timezone-geo/llms.txt) · [OpenAPI document](https://skmtc.dev/duoleads/apis/duo-data-utilities-iban-isin-vat-tax-id-barcode-timezone-geo/revisions/18790d59615e?raw)
