Skip to content

URL Media Inspector — Complete API Reference ​

Purpose: This is a single-page, self-contained reference for the URL Media Inspector API. It contains everything needed to understand, integrate, and use the API programmatically.

Overview ​

URL Media Inspector is a REST API that inspects public URLs and returns structured media intelligence — MIME type, media category, playback strategy, metadata, thumbnails, blurhash placeholders, color palettes, and provider-native intelligence (YouTube, Vimeo, etc.).

Base URL:

https://api.urlmediainspector.dev

Swagger UI: https://api.urlmediainspector.dev/api-docs

OpenAPI Spec: https://api.urlmediainspector.dev/openapi.json


Authentication ​

No authentication is required. The API is publicly accessible.


Endpoints ​

POST /api/v1/inspect ​

Inspect a public URL and receive structured media metadata.

Query Parameters ​

ParameterTypeDefaultDescription
profilestringstandardEnrichment depth: fast, standard, full, or embed
fieldsstring—Comma-separated list of data namespaces to include (e.g., identity,semantic)
expandstring—Comma-separated list of extra data to include (e.g., rawHeaders)
debugbooleanfalseInclude debug/network information when true

Request Body ​

json
{
  "url": "https://example.com/media.mp4",
  "webhookUrl": "https://your-server.com/webhook"
}
FieldTypeRequiredDescription
urlstring (URI)✅ YesThe public URL to inspect. Must be http: or https:.
webhookUrlstring (URI)NoOptional webhook URL to receive async enrichment completion events.

Profiles ​

Profiles control payload richness and latency behavior. They are a pure view layer — they never mutate the underlying resource state.

ProfileLatencyHTTP StatusBehavior
fastMinimal200Returns immediately with lightweight metadata. Hides heavy preview fields for direct media. Provider-native previews (e.g., YouTube thumbnails) are preserved.
standardProgressive200 or 202Returns partial results immediately. If heavy enrichments are pending, returns 202 Accepted with a job reference. Pending fields show as null.
fullDeterministic200Blocks synchronously until ALL enrichments complete. Always returns completion: 1. Includes diagnostics.
embedMinimal200Specialized profile for UI rendering. Returns embed capabilities, iframe parameters, and verification status rather than media enrichments.

Fields Parameter ​

Use fields to request only specific data namespaces. The value is a comma-separated list of top-level data namespace names.

Available namespaces: identity, semantic, provider, playback, metadata, preview, diagnostics

Example: ?profile=full&fields=identity,metadata returns only data.identity and data.metadata.

When fields is omitted, all namespaces applicable to the profile are returned.


GET /api/v1/job/{id} ​

Check the status of an asynchronous enrichment job.

Path Parameters ​

ParameterTypeDescription
idstringThe job ID returned in a 202 response

Response ​

json
{
  "success": true,
  "job": {
    "id": "abc123def456",
    "status": "completed",
    "progress": 100,
    "createdAt": "2025-01-01T00:00:00.000Z",
    "completedAt": "2025-01-01T00:00:05.000Z"
  },
  "data": { }
}

Job status values: waiting, active, completed, failed, delayed.


Response Envelope ​

Every successful inspect response follows this structure:

jsonc
{
  // Layer 1: Lifecycle Truth
  "success": true,
  "responseSource": "fresh",       // "fresh" | "cache" | "inflight"
  "cached": false,

  // Layer 2: Processing State (canonical, profile-independent)
  "processing": {
    "status": "complete",          // "partial" | "complete"
    "completion": 1,               // 0.0 → 1.0
    "enrichmentsPending": false,
    "completedStages": ["semantic", "mediaFormat", "metadata:image", "preview:thumbnail", "preview:blurhash", "preview:palette"],
    "pendingStages": []
  },

  // Layer 3: Data Payload (profile-projected)
  "data": {
    "identity": { },
    "semantic": { },
    "provider": { },               // only for provider-detected URLs (YouTube, Vimeo, etc.)
    "playback": { },
    "metadata": { },
    "preview": { },
    "diagnostics": { }             // only in FULL profile
  },

  // Layer 4: Enrichment Provenance (immutable)
  "enrichmentSources": {
    "semantic": "fresh",
    "mediaFormat": "fresh"
  },

  // Layer 5: Projection Metadata
  "projection": {
    "profile": "full",
    "hiddenFields": []
  },

  // Layer 6: Async Job (only for 202 responses)
  "job": {
    "id": "job_abc123",
    "status": "processing"
  }
}

Data Namespaces ​

identity ​

Always present. Contains URL resolution information.

jsonc
{
  "identity": {
    "url": "https://youtu.be/GLTTI1NFHus",           // original input
    "finalUrl": "https://youtu.be/GLTTI1NFHus",       // after redirect resolution
    "canonicalUrl": "https://www.youtube.com/watch?v=GLTTI1NFHus",  // normalized (provider URLs only)
    "mime": "text/html"
  }
}

semantic ​

Always present. Classifies the media type.

jsonc
{
  "semantic": {
    "kind": "direct-media",          // "direct-media" | "embed-media" | "webpage"
    "semanticType": "video",         // "video" | "audio" | "image" | "embedded-video" | "embedded-audio"
    "title": "Video Title"           // when available (provider-native or extracted)
  }
}

provider ​

Present only for provider-detected URLs (YouTube, Vimeo, Spotify, etc.).

jsonc
{
  "provider": {
    "name": "youtube",
    "confidence": 1,
    "detectionMethod": "url-parser"
  }
}

playback ​

Always present. Describes how to render/play the media.

jsonc
{
  "playback": {
    "strategy": "native",            // "native" (direct playback) | "iframe" (embedded)
    "component": "video",            // "video" | "audio" | "img" | "iframe"
    "embedUrl": "https://...",       // for iframe strategy only
    "streamable": true,              // supports HTTP Range requests
    "downloadable": true             // direct download possible
  }
}

metadata ​

Always present. Contains file info and exactly one category-specific sub-object.

jsonc
// Image
{
  "metadata": {
    "filename": "photo.jpg",
    "size": { "bytes": 204800, "formatted": "200 KB" },
    "image": { "colorSpace": "srgb", "hasAlpha": false }
  }
}

// Video
{
  "metadata": {
    "filename": "video.mp4",
    "size": { "bytes": 56733664, "formatted": "54.1 MB" },
    "video": {
      "duration": { "seconds": 596, "formatted": "9:56" },
      "fps": 24,
      "bitrate": { "bps": 1000000, "formatted": "1 Mbps" }
    }
  }
}

// Audio
{
  "metadata": {
    "filename": "song.mp3",
    "size": { "bytes": 5242880, "formatted": "5 MB" },
    "audio": {
      "duration": { "seconds": 180, "formatted": "3:00" },
      "bitrate": { "bps": 128000, "formatted": "128 kbps" },
      "sampleRate": 44100
    }
  }
}

Cross-category sub-objects are mutually exclusive — video never has metadata.image, audio never has metadata.video.

preview ​

Contains visual enrichments. Varies by media category and profile.

jsonc
// Image/Video (FULL profile, complete)
{
  "preview": {
    "thumbnail": { "url": "/public/thumbnails/generated.jpg", "dimensions": "320x180" },
    "placeholder": { "type": "blurhash", "value": "LEHV6nWB2yk8pyo0adR*.7kCMdnj" },
    "palette": { "dominant": "#112233" },
    "dominantColor": "#112233"
  }
}

// YouTube (provider-native thumbnail)
{
  "preview": {
    "thumbnail": { "url": "https://i.ytimg.com/vi/GLTTI1NFHus/hqdefault.jpg" }
  }
}

// Audio (visual enrichments omitted, future fields are null)
{
  "preview": {
    "waveform": null,
    "spectrogram": null
  }
}

Preview visibility by profile:

ProfileDirect MediaProvider Media
fastpreview is hidden (hiddenFields: ["preview"])Provider-native thumbnails are preserved
standardnull if pending, populated if completePopulated
fullAlways populated (blocks until complete)Populated

diagnostics ​

Only included in full profile. Contains performance timings.

jsonc
{
  "diagnostics": {
    "timings": {
      "total": 1240,
      "semantic": 12,
      "mediaFormat": 85,
      "preview": 890
    }
  }
}

Nullability Semantics ​

The API uses strict nullability rules:

ValueMeaning
nullSupported enrichment, but not yet available (pending or planned).
OmittedUnsupported for this media category (e.g., thumbnail on audio).
Empty {}FORBIDDEN — never returned.
PopulatedEnrichment complete, data available.

HTTP Status Codes ​

StatusCondition
200Inspection complete (or served from cache)
202Enrichments in progress, job reference included (STANDARD profile, heavy media)
400Invalid URL (INVALID_URL) or malformed payload (INVALID_PAYLOAD)
403SSRF protection blocked the request (SSRF_BLOCKED)
404Remote server returned 404 (RESOURCE_NOT_FOUND)
408Remote server timeout (UPSTREAM_TIMEOUT)
413Remote file exceeds size limit (PAYLOAD_TOO_LARGE)
502Remote server returned invalid response (BAD_GATEWAY)

Error Response Format ​

json
{
  "status": "error",
  "error": {
    "code": "INVALID_URL",
    "message": "The provided URL string was malformed."
  }
}

Media Categories ​

The API detects and handles these media categories with category-specific enrichments:

CategoryEnrichmentsExample MIME
Imagethumbnail, blurhash, palette, image metadataimage/jpeg, image/png, image/webp
Videothumbnail, blurhash, palette, video metadata (duration, fps, bitrate)video/mp4, video/webm
Audioaudio metadata (duration, bitrate, sample rate). No visual enrichments.audio/mpeg, audio/ogg
Embedded VideoProvider-native thumbnail, oEmbed data, embed URLtext/html (YouTube, Vimeo)

Supported Providers ​

Provider-detected URLs receive enhanced intelligence via oEmbed and URL parsing:

ProviderDetectionIntelligence
YouTubeURL pattern matchingCanonical URL, native thumbnail, oEmbed title, embed URL
VimeoURL pattern matchingNative thumbnail, oEmbed data, embed URL
SpotifyURL pattern matchingoEmbed data, embed URL

Provider-native data (thumbnails, titles) is available immediately — even in fast profile — because it doesn't require heavy enrichment processing.


curl Examples ​

Inspect an image (FAST) ​

bash
curl -X POST "https://api.urlmediainspector.dev/api/v1/inspect?profile=fast" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://images.unsplash.com/photo-1707343843437-caacff5cfa74"}'

Inspect a video (FULL, with all enrichments) ​

bash
curl -X POST "https://api.urlmediainspector.dev/api/v1/inspect?profile=full" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4"}'

Inspect a YouTube URL ​

bash
curl -X POST "https://api.urlmediainspector.dev/api/v1/inspect?profile=fast" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"}'

Inspect with specific fields only ​

bash
curl -X POST "https://api.urlmediainspector.dev/api/v1/inspect?profile=full&fields=identity,metadata" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/video.mp4"}'

Check async job status ​

bash
curl "https://api.urlmediainspector.dev/api/v1/job/abc123def456"

Complete Response Examples ​

Image — FULL Profile (Complete) ​

json
{
  "success": true,
  "responseSource": "fresh",
  "cached": false,
  "processing": {
    "status": "complete",
    "completion": 1,
    "enrichmentsPending": false,
    "completedStages": ["semantic", "mediaFormat", "metadata:image", "preview:thumbnail", "preview:blurhash", "preview:palette"],
    "pendingStages": []
  },
  "data": {
    "identity": {
      "url": "https://www.geekygallery.in/cdn/shop/files/god_16.jpg?v=1726037386&width=1946",
      "finalUrl": "https://www.geekygallery.in/cdn/shop/files/god_16.jpg?v=1726037386&width=1946",
      "mime": "image/jpeg"
    },
    "semantic": { "kind": "direct-media", "semanticType": "image" },
    "playback": { "strategy": "native", "component": "img", "streamable": false, "downloadable": true },
    "metadata": {
      "filename": "god_16.jpg",
      "size": { "bytes": 204800, "formatted": "200 KB" },
      "image": { "colorSpace": "srgb", "hasAlpha": false }
    },
    "preview": {
      "thumbnail": { "url": "/public/thumbnails/generated.jpg", "dimensions": "320x180" },
      "placeholder": { "type": "blurhash", "value": "LEHV6nWB2yk8pyo0adR*.7kCMdnj" },
      "palette": { "dominant": "#112233" },
      "dominantColor": "#112233"
    },
    "diagnostics": {
      "timings": { "total": 1240, "semantic": 12, "mediaFormat": 85 }
    }
  },
  "enrichmentSources": {
    "semantic": "fresh",
    "mediaFormat": "fresh",
    "metadata:image": "fresh",
    "preview:thumbnail": "fresh",
    "preview:blurhash": "fresh",
    "preview:palette": "fresh"
  },
  "projection": { "profile": "full", "hiddenFields": [] }
}

YouTube — FAST Profile (Provider-Native) ​

json
{
  "success": true,
  "responseSource": "fresh",
  "cached": false,
  "processing": {
    "status": "complete",
    "completion": 1,
    "enrichmentsPending": false,
    "completedStages": ["semantic", "mediaFormat", "oembed"],
    "pendingStages": []
  },
  "data": {
    "identity": {
      "url": "https://youtu.be/GLTTI1NFHus?si=okdNMy6_p0VSqZJG",
      "finalUrl": "https://youtu.be/GLTTI1NFHus?si=okdNMy6_p0VSqZJG",
      "canonicalUrl": "https://www.youtube.com/watch?v=GLTTI1NFHus",
      "mime": "text/html"
    },
    "semantic": { "kind": "embed-media", "semanticType": "embedded-video", "title": "Provider Native Title" },
    "provider": { "name": "youtube", "confidence": 1, "detectionMethod": "url-parser" },
    "playback": {
      "strategy": "iframe",
      "component": "iframe",
      "embedUrl": "https://www.youtube.com/embed/GLTTI1NFHus",
      "streamable": false,
      "downloadable": false
    },
    "preview": {
      "thumbnail": { "url": "https://i.ytimg.com/vi/GLTTI1NFHus/hqdefault.jpg" }
    }
  },
  "enrichmentSources": { "semantic": "fresh", "mediaFormat": "fresh", "oembed": "fresh" },
  "projection": { "profile": "fast", "hiddenFields": [] }
}

Video — STANDARD Profile (Async / 202 Response) ​

json
{
  "success": true,
  "responseSource": "fresh",
  "processing": {
    "status": "partial",
    "completion": 0.33,
    "enrichmentsPending": true,
    "completedStages": ["semantic", "mediaFormat"],
    "pendingStages": ["metadata:video", "preview:thumbnail", "preview:blurhash", "preview:palette"]
  },
  "data": {
    "identity": {
      "url": "https://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4",
      "finalUrl": "https://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4",
      "mime": "video/mp4"
    },
    "semantic": { "kind": "direct-media", "semanticType": "video" },
    "playback": { "strategy": "native", "component": "video", "streamable": true, "downloadable": true },
    "metadata": { "filename": "BigBuckBunny_320x180.mp4", "size": { "bytes": 56733664, "formatted": "54.1 MB" } }
  },
  "message": "Initial inspection complete. Heavy enrichments started in background.",
  "job": { "id": "a1b2c3d4e5", "status": "processing" },
  "projection": { "profile": "standard", "hiddenFields": [] }
}

Audio — FULL Profile (Complete) ​

json
{
  "success": true,
  "responseSource": "fresh",
  "processing": {
    "status": "complete",
    "completion": 1,
    "enrichmentsPending": false,
    "completedStages": ["semantic", "mediaFormat", "metadata:audio"],
    "pendingStages": []
  },
  "data": {
    "identity": {
      "url": "http://commondatastorage.googleapis.com/codeskulptor-assets/Epoq-Lepidoptera.ogg",
      "finalUrl": "http://commondatastorage.googleapis.com/codeskulptor-assets/Epoq-Lepidoptera.ogg",
      "mime": "audio/ogg"
    },
    "semantic": { "kind": "direct-media", "semanticType": "audio" },
    "playback": { "strategy": "native", "component": "audio", "streamable": true, "downloadable": true },
    "metadata": {
      "filename": "Epoq-Lepidoptera.ogg",
      "size": { "bytes": 1024, "formatted": "1 KB" },
      "audio": {
        "duration": { "seconds": 30, "formatted": "0:30" },
        "bitrate": { "bps": 128000, "formatted": "128 kbps" },
        "sampleRate": 44100
      }
    },
    "preview": {
      "waveform": null,
      "spectrogram": null
    }
  },
  "projection": { "profile": "full", "hiddenFields": [] }
}

YouTube — EMBED Profile (Renderability Context) ​

json
{
  "success": true,
  "profile": "embed",
  "data": {
    "embedType": "embedded-video",
    "mime": "text/html",
    "provider": "youtube",
    "renderability": {
      "native": false,
      "iframe": { "supported": "verified", "confidence": 1 },
      "embeddable": { "status": "verified" }
    },
    "verification": {
      "providerVerified": true,
      "registryVerified": true,
      "networkVerified": true,
      "method": "registry"
    },
    "render": {
      "strategy": "iframe",
      "safeEmbed": true,
      "renderConfidence": { "level": "verified", "score": 0.95 },
      "tag": "iframe",
      "attributes": {
        "src": "https://www.youtube.com/embed/GLTTI1NFHus",
        "allowfullscreen": true
      }
    }
  }
}

Security Constraints ​

  • SSRF Protection: All target URLs are resolved and verified against an IP denylist (localhost, private subnets, metadata endpoints). Redirect chains are re-verified at every hop.
  • Protocols: Only http: and https: URLs are accepted.
  • Size Limit: Remote files exceeding 50 MB are rejected.
  • Timeouts: Outbound requests timeout after 10 seconds.
  • Headers: Identifying headers are stripped from outbound requests.

Integration Patterns ​

Pattern 1: Quick metadata check (FAST) ​

Use fast profile when you only need basic media type detection, MIME, and playback strategy. Ideal for URL previews and media classification.

Pattern 2: Full enrichment (FULL) ​

Use full profile when you need complete metadata including thumbnails, blurhash, palettes, and codec details. The request blocks until everything is ready.

Pattern 3: Async enrichment (STANDARD) ​

Use standard profile for heavy media (video, large images). The API returns partial results immediately (202) with a job.id. Poll GET /api/v1/job/{id} for completion, or provide a webhookUrl to receive a callback when enrichments finish.

POST /api/v1/inspect?profile=standard  →  202 { job: { id: "xyz" } }
GET  /api/v1/job/xyz                   →  200 { job: { status: "completed" }, data: { ... } }

Infrastructure Documentation — Behavior-Driven, Schema-Governed