API reference

Programmatic access to Uruguayan customs declarations. Base URL https://uytrades.com/api/v1. Responses are application/json; dates are ISO 8601.

Authentication

Send a personal access token in the Authorization: Bearer <token> header. Tokens are created at /account. The plaintext value is returned once, at creation, and cannot be retrieved afterwards.

curl -H "Authorization: Bearer uytr_a1b2c3d4_xxxxxxxxxxxx" \
     "https://uytrades.com/api/v1/me"

A token inherits the plan of the account that created it. If the subscription lapses, Pro endpoints return 402 for that token; no revocation is needed.

Session cookie authentication is also accepted, so endpoints can be opened directly in a signed-in browser. Use a token for non-interactive clients.

Rate limits

Limits apply per account, across all of its tokens. Creating additional tokens does not raise them.

Endpoints Per minute Per hour
/dua, /dua/summary 20 60
/me, /dua/meta 60 600
Unauthenticated (reaches /dua/meta only) 10 60

Both windows apply simultaneously. The two endpoint groups are metered independently: exhausting one does not affect the other.

Responses from these endpoints carry:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1755792000
X-RateLimit-Limit-Minute: 20
X-RateLimit-Remaining-Minute: 19

Exceeding either window returns 429. Retry-After is the number of seconds until that window resets.

HTTP/1.1 429 Too Many Requests
Retry-After: 412

{
  "error": {
    "code": "rate_limited",
    "message": "60 requests/hour exceeded.",
    "limit": 60,
    "window": "hour",
    "reset": 1755792000
  }
}

Endpoints

GET /api/v1/me

Returns the authenticated account's id, email, plan and expiry.

curl -H "Authorization: Bearer $TOKEN" "https://uytrades.com/api/v1/me"

{"id": 42, "email": "you@example.com", "tier": "pro",
 "is_pro": true, "pro_until": "2027-01-14T00:00:00+00:00"}

GET /api/v1/dua/meta

Filter options, row caps and column names. Requires no authentication.

curl "https://uytrades.com/api/v1/dua/meta"

{"row_limit_max": 10000, "default_limit": 100,
 "regimens": ["ALL", "EXPORT", "IMPORT", "TRANSIT"],
 "columns": ["anio", "fecha_dua", "aduana_ing_egr", ...],
 "requires_pro": true, "is_pro": false}

GET /api/v1/dua

Returns declaration rows matching the filters, ordered by fecha_dua DESC, nro_publico. Requires Pro.

Parameter Required Description
yearyesInteger. 2016 or later.
regimenyesOne of IMPORT, EXPORT, TRANSIT, ALL.
aduananoThree-digit customs office code. Omitted means all offices.
searchnoCase-insensitive substring match on company name, or prefix match on NCM code. Wildcard characters are escaped. Truncated to 100 characters.
limitnoRows per page. 1 to 10,000. Default 100.
offsetnoRows skipped before the first row returned. Default 0.
curl -H "Authorization: Bearer $TOKEN" \
  "https://uytrades.com/api/v1/dua?year=2026&regimen=IMPORT&limit=100"

{"data": [{"anio": 2026, "fecha_dua": "2026-03-04", "nombre": "ACME SA", ...}],
 "pagination": {"limit": 100, "offset": 0, "returned": 100, "has_more": true},
 "query": {"year": 2026, "aduana": null, "regimen": "IMPORT", "search": null},
 "meta": {"elapsed_ms": 84.2}}

has_more is false on the last page, including when the total is an exact multiple of limit. Offsets are stable within a page sequence as long as the filters do not change.

GET /api/v1/dua/summary

Aggregates the same filter set without returning rows: total count, total customs value in USD, the ten customs offices with the highest value, and a count per régimen. Requires Pro.

curl -H "Authorization: Bearer $TOKEN" \
  "https://uytrades.com/api/v1/dua/summary?year=2026&regimen=IMPORT"

{"row_count": 148230, "total_usd": 2841993022.14,
 "by_aduana": [{"aduana": "001", "rows": 91204, "usd": 1922841003.5}],
 "by_regimen": [{"regimen_tipo": "I", "rows": 148230}],
 "query": {"year": 2026, "aduana": null, "regimen": "IMPORT", "search": null}}

GET POST DELETE /api/v1/keys

Lists, creates and revokes tokens for the authenticated account. POST returns the plaintext token in the response body; it is not retrievable later. Creating requires Pro.

curl -X POST -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"name": "nightly-etl"}' \
  "https://uytrades.com/api/v1/keys"

{"id": 7, "name": "nightly-etl", "prefix": "a1b2c3d4",
 "created_at": "2026-08-21T16:04:11+00:00", "last_used_at": null,
 "token": "uytr_a1b2c3d4_xxxxxxxxxxxx"}

curl -X DELETE -H "Authorization: Bearer $TOKEN" \
  "https://uytrades.com/api/v1/keys/7"

Maximum 20 active tokens per account. Revoke one before creating another.

Errors

Every error uses the same envelope. code is stable and safe to branch on; status repeats the HTTP status. Some errors add fields, such as upgrade_url on 402 and reset on 429.

{"error": {"code": "unauthorized", "status": 401, "message": "Authentication required. ..."}}
Status code When
400bad_requestMissing or malformed parameter. message identifies which.
401unauthorizedAbsent, malformed, unknown or revoked token, or a disabled account.
402payment_requiredAuthenticated, but the endpoint requires Pro. Body carries upgrade_url.
404not_foundUnknown endpoint or resource.
429rate_limitedRate limit exceeded. See Retry-After.
500server_errorServer error.

Authentication failures return 401 with a JSON body and a WWW-Authenticate: Bearer header. No endpoint under /api/v1 responds with a redirect, so clients may follow redirects safely without masking an auth error.

Bulk downloads

This API is designed for scoped queries. At 60 requests per hour and 10,000 rows per request, retrieving a full year of declarations is impractical.

Use the CSV export at /app/export instead. It accepts the same Bearer token and streams up to 1,000,000 rows in a single request.

curl -H "Authorization: Bearer $TOKEN" \
  "https://uytrades.com/app/export?year=2026&regimen=IMPORT&search=ACME" \
  -o acme-2026.csv

What is stable

The endpoints on this page are stable. Response fields will not be removed or change type without notice.

Every other path under /api/v1 serves the web application or is in beta. Those are unversioned and may change or be withdrawn without notice:

  • /api/v1/alerts
  • /api/v1/watches
  • /api/v1/views
  • /api/v1/export-schedules

They accept the same authentication but are not covered by the guarantee above.

Beta

GET /api/v1/stories/candidate recomputes a single story candidate for the video pipeline's reconciliation step. It requires Pro, counts against the expensive budget, and returns application/json. Its response follows the story formats and may change without notice.

Parameters: format, period (year:YYYY or ytd:YYYY), the format's own parameters, and optionally through (an ISO 8601 date) and page_size. An unknown parameter returns 400.

curl -H "Authorization: Bearer $TOKEN" \
  "https://uytrades.com/api/v1/stories/candidate?format=A&period=year:2025&ncm=4011&regimen=I&page_size=10"