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
fieldsstringComma-separated list of data namespaces to include (e.g., identity,semantic)
expandstringComma-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