"error" key with machine-readable fields for programmatic handling.
Error Response Structure
| Field | Type | Description |
|---|---|---|
type | string | Snake_case error type slug (stable identifier for programmatic use, e.g. not_found, validation_error, rate_limited) |
title | string | Human-readable error title |
status | integer | HTTP status code |
detail | string | Specific error description for this instance |
instance | string | Request path that caused the error |
suggestion | string | Actionable fix guidance |
retryable | boolean | Whether the client should retry |
retry_after | integer or null | Seconds to wait before retry |
request_id | string | Unique request identifier for support (top-level, alongside error) |
Error Catalog
| Status | Type Slug | Retryable | Title | Example Message | Common Cause | Fix |
|---|---|---|---|---|---|---|
| 400 | validation_error | No | Validation failed | ”Parameter ‘limit’ must be between 1 and 100.” | Invalid query parameter, missing required field, malformed JSON body | Check the parameter types and ranges. See the endpoint documentation for accepted values. |
| 400 | id_type_mismatch | No | ID type mismatch | ”Expected an owner ID (own_*) but received tm_abc123.” | Using a trademark ID where an owner ID is expected (or vice versa) | Use the correct ID prefix for the endpoint. Owner endpoints expect own_*, trademark endpoints expect tm_*, etc. |
| 400 | cursor_expired | No | Cursor expired | ”The pagination cursor has expired. Start a new query.” | Pagination cursor is more than 24 hours old | Start a fresh pagination session. For long-running syncs, checkpoint and resume within the 24-hour window. |
| 401 | unauthorized | No | Authentication required | ”Invalid API key.” | Missing Authorization header, invalid key format, expired key, revoked key | Check that you are sending Authorization: Bearer sig_xxx with a valid key. Verify the key has not expired or been revoked. |
| 403 | forbidden | No | Insufficient scope | ”API key does not have the ‘trademarks:read’ scope.” | The API key is valid but lacks the required scope for this endpoint | Create a new API key with the required scopes, or update the existing key’s scopes via PATCH /v1/organization/api-keys/{id}. |
| 403 | plan_upgrade_required | No | Plan upgrade required | ”Portfolios is not available on your current plan. Upgrade to access this feature.” | The endpoint belongs to a feature that is not included in your current plan (e.g. portfolios, watches, saved searches, alerts, events) | Upgrade your plan to gain access. Contact sales if you need this capability enabled. Not retryable — the response will not change without a plan change. |
| 404 | not_found | No | Resource not found | ”Trademark tm_xxx does not exist.” | The ID does not exist, was deleted, or belongs to a different resource type | Verify the ID is correct. Use search to find the resource if you do not have the exact ID. |
| 409 | conflict | No | Conflict | ”Idempotency key ‘abc123’ was already used with a different request body.” | Reusing an Idempotency-Key header with a different request body | Generate a new unique idempotency key for each distinct request. If retrying the same request, use the same key AND the same body. |
| 409 | idempotency_processing | Yes | Idempotency key in use | ”A request with this idempotency key is currently being processed.” | A retry with the same Idempotency-Key landed while the original request was still in flight | Wait briefly for the original request to finish, then retry with the same key to receive its cached result. |
| 410 | entity_merged | No | Entity merged | ”Owner own_old123 has been merged into own_abc123.” | The owner/attorney was deduplicated, or a resolved entity (ent_*) was fused into another. The old ID is permanently redirected. | Follow the merged_into field in the error response to the canonical entity. Update any cached references. |
| 422 | entity_too_large | No | Entity too large | ”This entity spans 12,431 member owners. Listing trademarks across members is limited to entities with 10,000 member owners or fewer.” | An entity_id / entity_group filter, or GET /v1/entities/{id}/trademarks, hit a cap. The error.reason discriminator says which: member_owners_too_large (the entity/group resolved to more member owners than the cap — carries member_count / member_count_limit) or family_graph_too_large (the GLEIF family-graph walk exceeded its node/depth bound before owners were counted, fired by ?entity_group= — carries related_entity_limit / depth_limit). | Narrow the query with additional filters, query a specific member via GET /v1/owners/{id}/trademarks, or use ?entity_id= for a single entity instead of ?entity_group=. The caps bound fan-out cost; not retryable as-is. |
| 429 | rate_limited | Yes | Rate limit exceeded | ”Rate limit exceeded. Retry after 30 seconds.” | Too many requests within the rate limit window | Wait for retry_after seconds, then retry. See Rate Limits. |
| 500 | internal_error | Yes | Internal server error | ”An unexpected error occurred.” | Server-side bug or transient failure | Retry with exponential backoff. If the error persists, contact support with the request_id. |
| 503 | service_unavailable | Yes | Service unavailable | ”Database is temporarily unavailable.” | A backend dependency is down | Wait for retry_after seconds, then retry. If the errors persist, email support@signa.so with the request_id. |
| 504 | preview_timeout | No | Preview Timeout | ”The preview could not produce a result within the server-side time budget.” | A POST /v1/watches/preview query was too heavy to evaluate within the ~20s budget | Do not blind-retry (retryable: false). Narrow the query — add office/jurisdiction filters, reduce trial_window_days, or scope filters.trademarkIds. See Preview Watch. |
Handling Errors
Errors return non-2xx status codes. The response body always contains a structurederror object and a top-level request_id.
instanceof patterns and automatic retries.
Idempotency and 409 Conflict
Mutating requests (PATCH, DELETE, and non-exempt POST) require an Idempotency-Key header (1–255 characters of [A-Za-z0-9_-]):
Idempotent-Replayed: true response header and preserve the
original request_id.
Same key + different body = 409 conflict error.
Same key while the first request is still in flight = 409 idempotency_processing — wait for the original to finish, then retry.
Idempotency keys are scoped to your organization and expire after 24 hours.
Only successful (2xx) responses are cached — a failed request never pins you
to its error.
POST /v1/trademarks(search)POST /v1/trademarks/batch(batch lookup)POST /v1/suggest(and other suggest endpoints)POST /v1/watches/preview(dry-run match count)POST /v1/alerts/lookup(bulk read by IDs)
[A-Za-z0-9_-]); a malformed value returns 400 even there.
POST /v1/organization/api-keys and POST /v1/organization/api-keys/{id}/rotate are not exempt. Replay is the
correct semantic for endpoints that mint one-time secrets: retrying the
same Idempotency-Key returns the same secret instead of minting a
second credential.Troubleshooting by Symptom
I'm getting 401 on every request
I'm getting 401 on every request
Check your Authorization header format. It must be
Authorization: Bearer sig_xxx (with the Bearer prefix). Common mistakes:- Missing
Bearerprefix:Authorization: sig_xxx - Wrong key prefix: only keys of the form
sig_{48 hex chars}are accepted. Any other prefix is rejected with 401 before any database lookup. - Expired key: check
expires_aton your API key - Revoked key: check the API key status in your dashboard
I'm getting 403 'insufficient scope'
I'm getting 403 'insufficient scope'
Your API key is valid but does not have the required scope for this endpoint. Each endpoint requires specific scopes:
- Trademark search, list, and entity GET endpoints need
trademarks:read - The event feed (
/v1/events) needsevents:read - Watches, alerts, webhooks, portfolios, and saved searches need
portfolios:manage - Watch replay needs
watches:admin(not self-service — request via support@signa.so) - API key management needs
api-keys:manage - Usage, plan, and log endpoints need
billing:read
I'm getting 400 'validation_error' but my request looks correct
I'm getting 400 'validation_error' but my request looks correct
Common causes:
- Missing required filter:
GET /v1/trademarksrequires at least one filter (no unscoped list queries). - Wrong date format: Use ISO 8601 (
2026-03-24or2026-03-24T12:00:00Z). - Array syntax: Bracketed keys (e.g.
offices[]=uspto) are not accepted; repeated keys (e.g.offices=uspto&offices=euipo) are accepted as a fallback, but the canonical form is comma-separated (offices=uspto,euipo). - Date range syntax: Use flat underscore operators, not brackets:
?filing_date_gte=2020-01-01&filing_date_lt=2025-01-01. - Boolean values: Only the literal strings
trueandfalseare accepted.1,0,yes,no, andTRUEall return a validation error. - application_number without office: When filtering by
application_numberorregistration_number, you must also specifyoffice. - Missing Idempotency-Key: Mutating requests (
PATCH,DELETE, and non-exemptPOSTs) require this header. Read-shaped POSTs (search, batch, suggest, watch preview, alert lookup) do not.
I'm getting 410 'entity_merged' for an owner I used yesterday
I'm getting 410 'entity_merged' for an owner I used yesterday
Entity resolution runs periodically. When two owner records are identified as the same entity, one is merged into the other. The
410 response includes a merged_into field pointing to the canonical record.Update your cached ID to the new one. All trademarks previously associated with the old owner are now under the canonical record.Resolved entities (ent_*) can also 410. When two resolved entities are fused, the loser’s ent_ id returns 410 entity_merged with the successor in merged_into. Note that a derived ent_<owner-uuid> for an owner that has since been linked does not 410 — it transparently resolves to the real entity (the returned id may differ from the one you requested).Search returns different results than the detail endpoint
Search returns different results than the detail endpoint
This is expected behavior due to the eventual consistency model. Search results are eventually consistent (typically under 30 seconds lag after a write), while detail endpoints are immediately consistent.If you just updated a record, wait a few seconds and retry the search. For time-sensitive workflows, use the detail endpoint as the source of truth.
I'm hitting rate limits during bulk operations
I'm hitting rate limits during bulk operations
For bulk lookups, use Batch Get Trademarks (up to 100 IDs per request) instead of individual GET requests. This counts as one request against your rate limit.For search-heavy workloads, consider caching results on your side, using cursor pagination instead of re-executing searches, or upgrading your plan.
I'm getting 503 errors intermittently
I'm getting 503 errors intermittently
This indicates a transient backend issue. The error is retryable — wait for the
retry_after period and retry with exponential backoff. If 503 errors persist for more than a few minutes, email support@signa.so with a request_id from one of the failed calls.