The enrichment API lets you trigger AI analysis of saved URLs and monitor job progress.
Enqueue Enrichment
Queue a bookmark for AI enrichment. The server validates the URL, checks quotas, and either returns a cached result or starts processing.
curl -X POST \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
"https://api.unisave.io/v1/enrichment/enqueue" \
-d '{
"clientBookmarkId": "abc-123",
"url": "https://youtube.com/watch?v=dQw4w9WgXcQ",
"clientSavedAt": "2026-04-12T10:30:00Z"
}'
Request Body
| Field | Type | Required | Description |
|---|
clientBookmarkId | string | Yes | Must match ^[a-zA-Z0-9_-]{1,128}$ |
url | string | Yes | Valid http: or https: URL |
clientSavedAt | string | Yes | ISO 8601 timestamp |
Response — Cache Hit 200
{
"cacheHit": true,
"status": "completed"
}
When the normalized URL has been enriched before (by any user), the cached result is applied instantly — no job is created.
Response — Queued 202
{
"accepted": true,
"status": "pending"
}
A new enrichment job is created. The bookmark will be updated asynchronously when processing completes.
Behavior Details
The enqueue endpoint performs these checks in order:
- URL validation — must be valid
http:/https: scheme
- URL normalization — strips tracking params, fragments,
www. prefix
- Cache lookup — if the normalized URL has cached results, applies them immediately
- Duplicate check — detects if the user already has this normalized URL
- Existing job check — skips if a pending/processing job already exists
- Quota check — enforces free-tier limits (see below)
- Job creation — creates the enrichment job with status
pending
Consent Gate
The useAi consent gate must be enabled for enrichment to proceed. If the user hasn’t consented to AI processing, the request is rejected.
Get Enrichment Status
Check the status of an enrichment job and retrieve results.
curl -H "Authorization: Bearer $ACCESS_TOKEN" \
"https://api.unisave.io/v1/enrichment/status?clientBookmarkId=abc-123"
Query Parameters
| Param | Type | Required | Description |
|---|
clientBookmarkId | string | Yes | The bookmark to check |
Response 200
{
"data": {
"clientBookmarkId": "abc-123",
"status": "completed",
"result": {
"normalizedUrl": "https://youtube.com/watch?v=dQw4w9WgXcQ",
"domain": "youtube.com",
"title": "Rick Astley - Never Gonna Give You Up",
"description": "The official video for Rick Astley's...",
"thumbnailUrl": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
"summary": "Rick Astley's iconic 1987 music video...",
"saveWhy": "Classic music video reference",
"tags": ["music", "video", "80s", "pop"],
"snapshotStatus": "completed"
},
"errorCode": null
}
}
| Field | Type | Description |
|---|
clientBookmarkId | string | The bookmark queried |
status | string | Job status (see table below) |
result | object | null | Enrichment data when available. null while pending/processing. |
errorCode | string | null | Error code when enrichment fails. null on success. |
The saveWhy field inside result is Pro-gated. Free users receive null for this field even when enrichment is complete. Upgrade to Pro to access it.
Status Values
| Status | Description |
|---|
pending | Job created, waiting to be picked up by worker |
processing | Worker is actively fetching and analyzing the URL |
retrying | Previous attempt failed, scheduled for retry |
completed | Enrichment finished successfully |
failed | All retry attempts exhausted |
Quotas
Free-tier users have enrichment quotas:
| Limit | Free | Pro |
|---|
| Per month | 200 | Unlimited |
| Per minute | 5 | Unlimited |
When quota is exceeded, the API returns 429 quota-exceeded:
{
"code": "quota-exceeded",
"message": "Monthly enrichment limit reached. Upgrade to Pro for unlimited enrichments."
}
Monthly quota resets on the 1st of each month (UTC).
Retry Policy
Failed enrichments are retried automatically:
| Attempt | Delay | Condition |
|---|
| 1st retry | 1 minute | If error is retriable |
| 2nd retry | 5 minutes | If error is retriable |
| 3rd failure | — | Permanent failure |
Retriable errors: network timeouts, DNS failures, HTTP 5xx, rate limits from content providers.
Non-retriable errors: HTTP 4xx, blocked hosts, invalid URLs, quota exhausted.