Skip to main content

File Signature (Magic Bytes) Detection API

REAL DATA

Detect a file's real format from its magic bytes and flag when the filename or declared MIME type disagrees. Every signature cited to its own spec.

Authoritative reference data or standards computation

Base URL
/api/file-signatures
Capabilities
4 routes
Last updated
July 30, 2026
Data sources · 4
+1 more
  • Curated for this API: the ranking rule, the container/brand model, the per-row caveats and the byte-count table

GET /api/file-signatures

Filters are ANDed and rows stay in table order (category, then formatId, then signatureId). With no filters this returns the whole table. No match is an empty list with a warning, never a substituted row.

Live requestRuns against the public API
No key required
GET/api/file-signatures?category=archive

Request parameters

Only signatures whose format is in this category.

Case- and diacritic-insensitive substring match against the format name, the formatId, any extension and the media type.

Only signatures whose format lists this conventional extension. A leading dot is accepted and stripped; matching is case-insensitive.

Only signatures decidable within the first N bytes of the file — that is, rows whose bytesNeeded ≤ N.

'only' keeps rows that refine another format's container (EPUB inside ZIP, AVIF inside ISO BMFF); 'exclude' keeps standalone signatures only.

Advanced response options6 options

Limit the number of returned records (1–100). Defaults to all matches.

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

Return all fields except these (comma-separated).

Response format: json envelope, ndjson (one record per line) or csv.

Pretty-print the JSON response.

Drop the envelope: return the raw array/object without data/meta wrapper.

Response

Example parameters are ready. Send the request to inspect the live response.

Route reference

Code samples Ready-to-copy requests in 4 languages
Choose a code sample language
curl "https://randomapi.dev/api/file-signatures?category=archive"
const res = await fetch("https://randomapi.dev/api/file-signatures?category=archive");
const { data, meta } = await res.json();
import requests

data = requests.get("https://randomapi.dev/api/file-signatures?category=archive").json()["data"]
$json = json_decode(file_get_contents(
  "https://randomapi.dev/api/file-signatures?category=archive"
), true);
$data = $json["data"];
Parameters Route-specific request inputs 5
category enum

Only signatures whose format is in this category.

default: any
allowed: image | audio | video | archive | compression | document | executable | font | database | disk-image | data | any
example: category=archive
search string

Case- and diacritic-insensitive substring match against the format name, the formatId, any extension and the media type.

allowed: 1 – 64
example: search=zip
extension string

Only signatures whose format lists this conventional extension. A leading dot is accepted and stripped; matching is case-insensitive.

allowed: 1 – 16
example: extension=webp
maxOffsetBytes int

Only signatures decidable within the first N bytes of the file — that is, rows whose bytesNeeded ≤ N.

allowed: 1 – 65536
example: maxOffsetBytes=16
containers enum

'only' keeps rows that refine another format's container (EPUB inside ZIP, AVIF inside ISO BMFF); 'exclude' keeps standalone signatures only.

default: all
allowed: all | only | exclude
example: containers=only
Universal parameters Shared response and formatting options 6
count int

Limit the number of returned records (1–100). Defaults to all matches.

default: all matches
allowed: 1 – 100
example: count=3
fields list

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

example: fields=signatureId,formatId
exclude list

Return all fields except these (comma-separated).

example: exclude=sourceKind
format enum

Response format: json envelope, ndjson (one record per line) or csv.

default: json
allowed: json | ndjson | csv
example: format=csv
pretty boolean

Pretty-print the JSON response.

default: false
example: pretty=true
unwrap boolean

Drop the envelope: return the raw array/object without data/meta wrapper.

default: false
example: unwrap=true
Response schema Fields returned in each record 16
signatureId string

Stable kebab-case id of this byte pattern; the join key used by /lookup and /detect.

example: isobmff-ftyp-avif

formatId string

Stable kebab-case id of the format this pattern identifies. Several signatures may share one formatId (TIFF has two byte orders, RAR two generations, Mach-O four magics).

example: avif

formatName string

The format's name as its own specification writes it.

example: AV1 Image File Format

category string

One of: image | audio | video | archive | compression | document | executable | font | database | disk-image | data. ('any' is a filter value only, never a row value.)

example: image

offset integer

Byte offset of the pattern from the start of the file. 0 for most; 4 for the ISO BMFF ftyp box type, 36 for the ICC profile signature, 257 for POSIX ustar, 32769 for ISO 9660.

example: 4

pattern string

The bytes, as space-separated uppercase hex pairs. `??` means "any byte" — a length or version field the format does not fix.

example: 66 74 79 70 61 76 69 66

bytesNeeded integer

offset + pattern length: how many leading bytes you must have read before this signature can be evaluated at all.

example: 12

literalBytes integer

Number of non-wildcard bytes in the pattern. This is the ranking weight /detect uses — more literal evidence ranks higher.

example: 8

extensions string[]

Conventional extensions (lowercase, no dot, sorted). Empty for formats with no conventional extension. Conventions, not guarantees — the whole point of this endpoint is that an extension can lie.

example: ["avif","avifs"]

mimeType string nullable

The media type the format's own registration declares — or, absent a registration, the single type it is universally served as (audio/wav, application/x-tar) — and only when that exact string exists in this API's IANA media-types dataset. null in three cases, each deliberate: the format has neither a registration nor one de-facto type (ELF, Mach-O, Java class, ar, DEX, LZ4); the row identifies a container or tag that can wrap anything, so no type identifies it (CFB, ID3v2, the bare RIFF and ftyp rows, MBR/GPT); or the extension is registered to something unrelated (PSB). Never invented.

example: image/avif

containerFormatId string nullable

The formatId of the container this signature sits inside, when it refines one — 'zip' for EPUB, 'isobmff' for AVIF/HEIC/MP4, 'riff' for WAV/WebP/AVI, 'jpeg' for JFIF/Exif. null for standalone signatures.

example: isobmff

trailer string nullable

The end-of-file marker the format's spec defines, as hex ('3B' for GIF, '25 25 45 4F 46' for PDF's %%EOF). Recorded because upload validators ask for it; /detect never checks it, because /detect sees a prefix, not a whole file. null where the spec defines none.

example: null

note string nullable

What this signature does NOT tell you: the row's own caveat followed by the format's, when both exist — a per-row note never hides the format-level one. null when there is nothing honest to add.

example: Brand "avif".

specification string

Verbatim citation of the document AND clause the byte values were read from. Never a paraphrase, never a secondary source.

example: MP4 Registration Authority, registered file-type brands (verified 2026-07-30); box structure per ISO/IEC 14496-12, "File Type Box"

specificationUrl string nullable

HTTPS link to that document, or null when the only authoritative copy is paywalled (ITU-T T.81, ISO/IEC 14496-12) and no legitimate free copy exists. A decorative link would be worse than null.

example: https://mp4ra.org/registered-types/brands

sourceKind string

Where the citation sits on the authority ladder: standard (ISO/IEC, ITU-T, IETF, ECMA, W3C, POSIX), vendor-specification (Microsoft Open Specifications, Adobe, Google, RARLAB), registry (MP4RA, IANA), reference-implementation (bzip2, 7-Zip, Apple's loader.h — formats with no published specification).

example: registry

Documented examples

Build-generated requests and complete responses
5
Every archive signature
GET /api/file-signatures?category=archive
{
  "data": [
    {
      "signatureId": "sevenzip-signature",
      "formatId": "7z",
      "formatName": "7z archive",
      "category": "archive",
      "offset": 0,
      "pattern": "37 7A BC AF 27 1C",
      "bytesNeeded": 6,
      "literalBytes": 6,
      "extensions": [
        "7z"
      ],
      "mimeType": "application/x-7z-compressed",
      "containerFormatId": null,
      "trailer": null,
      "note": null,
      "specification": "7-Zip, DOC/7zFormat.txt — SignatureHeader kSignature = \"7\" \"z\" 0xBC 0xAF 0x27 0x1C (7z has no published specification)",
      "specificationUrl": "https://github.com/ip7z/7zip/blob/main/DOC/7zFormat.txt",
      "sourceKind": "reference-implementation"
    },
    {
      "signatureId": "ar-magic",
      "formatId": "ar",
      "formatName": "Unix archive (ar)",
      "category": "archive",
      "offset": 0,
      "pattern": "21 3C 61 72 63 68 3E 0A",
      "bytesNeeded": 8,
      "literalBytes": 8,
      "extensions": [
        "a",
        "ar"
      ],
      "mimeType": null,
      "containerFormatId": null,
      "trailer": null,
      "note": "No formal standard exists for ar, and there is no registered or de-facto media type for it. This row is also the first 8 bytes of a Debian package.",
      "specification": "FreeBSD ar(5), \"ar\" — the archive begins with the magic string \"!<arch>\" followed by a newline; the ar format has no formal standard",
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
What can you decide from the first 16 bytes
GET /api/file-signatures?maxOffsetBytes=16
{
  "data": [
    {
      "signatureId": "isobmff-ftyp-avif",
      "formatId": "avif",
      "formatName": "AV1 Image File Format",
      "category": "image",
      "offset": 4,
      "pattern": "66 74 79 70 61 76 69 66",
      "bytesNeeded": 12,
      "literalBytes": 8,
      "extensions": [
        "avif",
        "avifs"
      ],
      "mimeType": "image/avif",
      "containerFormatId": "isobmff",
      "trailer": null,
      "note": "Brand \"avif\".",
      "specification": "MP4 Registration Authority, registered file-type brands (verified 2026-07-30); box structure per ISO/IEC 14496-12, \"File Type Box\"",
      "specificationUrl": "https://mp4ra.org/registered-types/brands",
      "sourceKind": "registry"
    },
    {
      "signatureId": "isobmff-ftyp-avis",
      "formatId": "avif",
      "formatName": "AV1 Image File Format",
      "category": "image",
      "offset": 4,
      "pattern": "66 74 79 70 61 76 69 73",
      "bytesNeeded": 12,
      "literalBytes": 8,
      "extensions": [
        "avif",
        "avifs"
      ],
      "mimeType": "image/avif",
      "containerFormatId": "isobmff",
      "trailer": null,
      "note": "Brand \"avis\" — an AVIF image sequence.",
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Signatures that only exist inside another container
GET /api/file-signatures?containers=only
{
  "data": [
    {
      "signatureId": "isobmff-ftyp-avif",
      "formatId": "avif",
      "formatName": "AV1 Image File Format",
      "category": "image",
      "offset": 4,
      "pattern": "66 74 79 70 61 76 69 66",
      "bytesNeeded": 12,
      "literalBytes": 8,
      "extensions": [
        "avif",
        "avifs"
      ],
      "mimeType": "image/avif",
      "containerFormatId": "isobmff",
      "trailer": null,
      "note": "Brand \"avif\".",
      "specification": "MP4 Registration Authority, registered file-type brands (verified 2026-07-30); box structure per ISO/IEC 14496-12, \"File Type Box\"",
      "specificationUrl": "https://mp4ra.org/registered-types/brands",
      "sourceKind": "registry"
    },
    {
      "signatureId": "isobmff-ftyp-avis",
      "formatId": "avif",
      "formatName": "AV1 Image File Format",
      "category": "image",
      "offset": 4,
      "pattern": "66 74 79 70 61 76 69 73",
      "bytesNeeded": 12,
      "literalBytes": 8,
      "extensions": [
        "avif",
        "avifs"
      ],
      "mimeType": "image/avif",
      "containerFormatId": "isobmff",
      "trailer": null,
      "note": "Brand \"avis\" — an AVIF image sequence.",
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Find every signature for WebP
GET /api/file-signatures?extension=webp
{
  "data": [
    {
      "signatureId": "webp-riff-form",
      "formatId": "webp",
      "formatName": "WebP",
      "category": "image",
      "offset": 0,
      "pattern": "52 49 46 46 ?? ?? ?? ?? 57 45 42 50",
      "bytesNeeded": 12,
      "literalBytes": 8,
      "extensions": [
        "webp"
      ],
      "mimeType": "image/webp",
      "containerFormatId": "riff",
      "trailer": null,
      "note": "The four wildcard bytes are the RIFF file size.",
      "specification": "WebP Container Specification, \"WebP File Header\" — \"RIFF\", file size, then \"WEBP\"",
      "specificationUrl": "https://developers.google.com/speed/webp/docs/riff_container",
      "sourceKind": "vendor-specification"
    }
  ],
  "meta": {
    "endpoint": "file-signatures",
    "count": 1,
    "params": {
      "category": "any",
      "extension": "webp",
      "containers": "all"
    },
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Everything ZIP-flavoured, pattern and citation only
GET /api/file-signatures?search=zip&fields=signatureId,pattern,specification
{
  "data": [
    {
      "signatureId": "epub-ocf-mimetype",
      "pattern": "50 4B 03 04 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 6D 69 6D 65 74 79 70 65 61 70 70 6C 69 63 61 74 69 6F 6E 2F 65 70 75 62 2B 7A 69 70",
      "specification": "EPUB Open Container Format 3.2, §4.3 \"OCF ZIP Container Media Type Identification\" — the \"mimetype\" file MUST be the first file, uncompressed, with no extra field, and contain \"application/epub+zip\""
    },
    {
      "signatureId": "zip-end-of-central-directory",
      "pattern": "50 4B 05 06",
      "specification": ".ZIP File Format Specification, APPNOTE 6.3.10 (2022-11-01), §4.3.16 \"End of central directory record\" — signature 0x06054b50"
    },
    {
      "signatureId": "zip-local-file-header",
      "pattern": "50 4B 03 04",
      "specification": ".ZIP File Format Specification, APPNOTE 6.3.10 (2022-11-01), §4.3.7 \"Local file header\" — signature 0x04034b50"
    },
    {
      "signatureId": "bzip2-stream-header",
      "pattern": "42 5A 68 ??",
      "specification": "bzip2 1.0.8 reference implementation (compress.c / decompress.c) — the stream header is \"B\" \"Z\" \"h\" followed by the block-size digit \"1\"–\"9\"; bzip2 has no published specification"
    },
    {
      "signatureId": "gzip-member-header",
      "pattern": "1F 8B",
      "specification": "RFC 1952 §2.3.1 \"Member header and trailer\" — ID1 = 0x1f, ID2 = 0x8b"
    }
  ],
  "meta": {
    "endpoint": "file-signatures",
    "count": 5,
    "params": {
      "category": "any",
      "search": "zip",
      "containers": "all"
    },
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}

GET /api/file-signatures/detect

Returns every signature the supplied prefix satisfies, ranked by literal evidence. Nothing is fetched and nothing is stored. A malformed claimedType is deliberately NOT a 400 — a garbage Content-Type is the finding you came for, so it is reported inside mismatch and the request still succeeds. mismatch says 'disagrees' only when the bytes genuinely refute the claim; a claim this table cannot decide (DOCX vs a plain ZIP, WebM vs Matroska, or a signature whose deciding bytes lie past your prefix) comes back as null with a reason, never as an accusation.

Live requestRuns against the public API
No key required
GET/api/file-signatures/detect?bytes=89504e470d0a1a0a

Request parameters

The first bytes of the file, from offset 0. Hex or base64 per `encoding`. Hex accepts upper/lower case, an optional leading '0x', and space, ':' or '-' separators (so xxd/hexdump output pastes straight in); base64 accepts the RFC 4648 base64 or base64url alphabet, padded or unpadded, with whitespace ignored. 8,192 characters is 4,096 bytes of hex or about 6,144 bytes of base64.

How `bytes` is encoded.

The filename the client claimed (an upload's `filename`). Only its basename and extension are used; the longest suffix this table registers wins, so 'backup.tar.gz' resolves to 'gz'. Never fetched, never written anywhere.

The Content-Type the client declared. Parameters (';charset=…') are ignored for comparison. A malformed value is reported in `mismatch`, not rejected with a 400. Percent-encode a structured suffix's '+' as %2B (`application%2Fepub%2Bzip`): a literal '+' in a query string means a space, and a space is never legal in a media type — if that happens the endpoint recovers the '+' and says so in `mismatch.reasons`.

Advanced response options4 options

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

Return all fields except these (comma-separated).

Pretty-print the JSON response.

Drop the envelope: return the raw array/object without data/meta wrapper.

Response

Example parameters are ready. Send the request to inspect the live response.

Route reference

Code samples Ready-to-copy requests in 4 languages
Choose a code sample language
curl "https://randomapi.dev/api/file-signatures/detect?bytes=89504e470d0a1a0a"
const res = await fetch("https://randomapi.dev/api/file-signatures/detect?bytes=89504e470d0a1a0a");
const { data, meta } = await res.json();
import requests

data = requests.get("https://randomapi.dev/api/file-signatures/detect?bytes=89504e470d0a1a0a").json()["data"]
$json = json_decode(file_get_contents(
  "https://randomapi.dev/api/file-signatures/detect?bytes=89504e470d0a1a0a"
), true);
$data = $json["data"];
Parameters Route-specific request inputs 4
bytes string required

The first bytes of the file, from offset 0. Hex or base64 per `encoding`. Hex accepts upper/lower case, an optional leading '0x', and space, ':' or '-' separators (so xxd/hexdump output pastes straight in); base64 accepts the RFC 4648 base64 or base64url alphabet, padded or unpadded, with whitespace ignored. 8,192 characters is 4,096 bytes of hex or about 6,144 bytes of base64.

allowed: 1 – 8192
example: bytes=89504e470d0a1a0a
encoding enum

How `bytes` is encoded.

default: hex
allowed: hex | base64
example: encoding=base64
claimedName string

The filename the client claimed (an upload's `filename`). Only its basename and extension are used; the longest suffix this table registers wins, so 'backup.tar.gz' resolves to 'gz'. Never fetched, never written anywhere.

allowed: 1 – 255
example: claimedName=avatar.png
claimedType string

The Content-Type the client declared. Parameters (';charset=…') are ignored for comparison. A malformed value is reported in `mismatch`, not rejected with a 400. Percent-encode a structured suffix's '+' as %2B (`application%2Fepub%2Bzip`): a literal '+' in a query string means a space, and a space is never legal in a media type — if that happens the endpoint recovers the '+' and says so in `mismatch.reasons`.

allowed: 1 – 255
example: claimedType=image/png
Universal parameters Shared response and formatting options 4
fields list

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

example: fields=bytesProvided,encoding
exclude list

Return all fields except these (comma-separated).

example: exclude=mismatch
pretty boolean

Pretty-print the JSON response.

default: false
example: pretty=true
unwrap boolean

Drop the envelope: return the raw array/object without data/meta wrapper.

default: false
example: unwrap=true
Response schema Fields returned in each record 12
bytesProvided integer

How many bytes `bytes` decoded to.

example: 8

encoding string

The resolved `encoding`.

example: hex

hexPreview string

The first up to 32 decoded bytes as space-separated uppercase hex, so you can see exactly what was parsed. Truncated with "…" beyond that.

example: 89 50 4E 47 0D 0A 1A 0A

matches object[]

Every signature these bytes satisfy, ranked by literalBytes descending, then offset ascending, then signatureId ascending. Capped at 12 entries (a warning names how many were dropped). Empty array when nothing matches. Element keys: `signatureId`, `formatId`, `formatName`, `category`, `offset`, `pattern`, `matchedBytes` (the bytes actually present at that offset), `bytesNeeded`, `literalBytes`, `mimeType`, `extensions`, `containerFormatId`, `note`, `specification`, `specificationUrl`.

example: [{"signatureId":"png-signature","formatId":"png","formatName":"Portable Network Graphics","category":"image","offset":0,"pattern":"89 50 4E 47 0D 0A 1A 0A","matchedBytes":"89 50 4E 47 0D 0A 1A 0A","bytesNeeded":8,"literalBytes":8,"mimeType":"image/png","extensions":["png"],"containerFormatId":null,"note":null,"specification":"W3C PNG Specification (Third Edition), §5.2 \"PNG signature\"","specificationUrl":"https://www.w3.org/TR/png-3/"}]

matchCount integer

Total matches found (may exceed matches.length when capped).

example: 1

best object nullable

matches[0], or null when nothing matched. The match with the most literal evidence — not a verdict. Same element shape as `matches`.

example: {"signatureId":"png-signature","formatId":"png","formatName":"Portable Network Graphics","category":"image","offset":0,"pattern":"89 50 4E 47 0D 0A 1A 0A","matchedBytes":"89 50 4E 47 0D 0A 1A 0A","bytesNeeded":8,"literalBytes":8,"mimeType":"image/png","extensions":["png"],"containerFormatId":null,"note":null,"specification":"W3C PNG Specification (Third Edition), §5.2 \"PNG signature\"","specificationUrl":"https://www.w3.org/TR/png-3/"}

ambiguous boolean

true when two or more matches remain that are NOT in a container/refinement relationship with each other — i.e. these bytes genuinely satisfy rival formats. 0xCAFEBABE (a Java class file and a Mach-O universal binary) is the canonical case.

example: false

notEvaluated integer

How many table rows could not be evaluated because their bytesNeeded exceeds bytesProvided. A zero-match answer with a non-zero notEvaluated is not evidence of an unknown format.

example: 24

bytesForWholeTable integer

The largest bytesNeeded in the table — how many leading bytes would make notEvaluated zero. Constant per deploy, and beyond the `bytes` cap by design while ISO 9660 is in the table.

example: 32774

claimedName string nullable

The basename of `claimedName` as supplied, or null. Never fetched, never written anywhere.

example: avatar.png

claimedType string nullable

`claimedType` as supplied, lowercased with parameters stripped, or null.

example: image/png

mismatch object nullable

null when neither claim was supplied. Otherwise `{ verdict: "agrees" | "disagrees" | "undetermined", claimedExtension, extensionAgrees, typeAgrees, reasons }`. A flag is `false` only when the bytes REFUTE the claim — every signature of every format that claims that extension or media type has a literal byte you supplied that contradicts it. It is `null` when the claim was not supplied, could not be parsed, names something this table has no signature for (DOCX, JAR, video/webm and audio/x-ms-wma are all indistinguishable from their container in a prefix), or is still consistent with a signature whose deciding bytes lie past the prefix you supplied. `verdict` is "undetermined" when nothing matched, when the claim could not be parsed, and whenever neither flag came out true or false. `reasons` is a bounded list of plain-English findings, each naming the claim and the evidence.

example: {"verdict":"disagrees","claimedExtension":"jpg","extensionAgrees":false,"typeAgrees":false,"reasons":["The bytes match Portable Network Graphics; the filename claims '.jpg', which this table registers only for JPEG (JFIF/Exif-agnostic baseline marker sequence), JPEG with an Exif APP1 segment — and every signature those formats define is contradicted by the bytes you supplied.","The declared Content-Type 'image/jpeg' belongs to JPEG (JFIF/Exif-agnostic baseline marker sequence), JPEG with a JFIF APP0 segment, JPEG with an Exif APP1 segment in this table, and every signature those formats define is contradicted by the bytes you supplied (which match image/png)."]}

Documented examples

Build-generated requests and complete responses
8
Identify a PNG from its first eight bytes
GET /api/file-signatures/detect?bytes=89504e470d0a1a0a
{
  "data": {
    "bytesProvided": 8,
    "encoding": "hex",
    "hexPreview": "89 50 4E 47 0D 0A 1A 0A",
    "matches": [
      {
        "signatureId": "png-signature",
        "formatId": "png",
        "formatName": "Portable Network Graphics",
        "category": "image",
        "offset": 0,
        "pattern": "89 50 4E 47 0D 0A 1A 0A",
        "matchedBytes": "89 50 4E 47 0D 0A 1A 0A",
        "bytesNeeded": 8,
        "literalBytes": 8,
        "mimeType": "image/png",
        "extensions": [
          "png"
        ],
        "containerFormatId": null,
        "note": null,
        "specification": "W3C PNG Specification (Third Edition), §5.2 \"PNG signature\"",
        "specificationUrl": "https://www.w3.org/TR/png-3/"
      }
    ],
    "matchCount": 1,
    "best": {
      "signatureId": "png-signature",
      "formatId": "png",
      "formatName": "Portable Network Graphics",
      "category": "image",
      "offset": 0,
      "pattern": "89 50 4E 47 0D 0A 1A 0A",
      "matchedBytes": "89 50 4E 47 0D 0A 1A 0A",
      "bytesNeeded": 8,
      "literalBytes": 8,
      "mimeType": "image/png",
      "extensions": [
        "png""generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
An upload whose filename and Content-Type lie
GET /api/file-signatures/detect?bytes=89504e470d0a1a0a&claimedName=avatar.jpg&claimedType=image/jpeg
{
  "data": {
    "bytesProvided": 8,
    "encoding": "hex",
    "hexPreview": "89 50 4E 47 0D 0A 1A 0A",
    "matches": [
      {
        "signatureId": "png-signature",
        "formatId": "png",
        "formatName": "Portable Network Graphics",
        "category": "image",
        "offset": 0,
        "pattern": "89 50 4E 47 0D 0A 1A 0A",
        "matchedBytes": "89 50 4E 47 0D 0A 1A 0A",
        "bytesNeeded": 8,
        "literalBytes": 8,
        "mimeType": "image/png",
        "extensions": [
          "png"
        ],
        "containerFormatId": null,
        "note": null,
        "specification": "W3C PNG Specification (Third Edition), §5.2 \"PNG signature\"",
        "specificationUrl": "https://www.w3.org/TR/png-3/"
      }
    ],
    "matchCount": 1,
    "best": {
      "signatureId": "png-signature",
      "formatId": "png",
      "formatName": "Portable Network Graphics",
      "category": "image",
      "offset": 0,
      "pattern": "89 50 4E 47 0D 0A 1A 0A",
      "matchedBytes": "89 50 4E 47 0D 0A 1A 0A",
      "bytesNeeded": 8,
      "literalBytes": 8,
      "mimeType": "image/png",
      "extensions": [
        "png"
    …
    ]
  }
}
Ambiguous bytes: Java class or Mach-O universal binary
GET /api/file-signatures/detect?bytes=cafebabe
{
  "data": {
    "bytesProvided": 4,
    "encoding": "hex",
    "hexPreview": "CA FE BA BE",
    "matches": [
      {
        "signatureId": "java-class-magic",
        "formatId": "java-class",
        "formatName": "Java class file",
        "category": "executable",
        "offset": 0,
        "pattern": "CA FE BA BE",
        "matchedBytes": "CA FE BA BE",
        "bytesNeeded": 4,
        "literalBytes": 4,
        "mimeType": null,
        "extensions": [
          "class"
        ],
        "containerFormatId": null,
        "note": "Compiled Java classes have no IANA registration, and no single type they are universally served as either — a class file is loaded by a JVM, not served over HTTP — so this row reports none rather than pick the unregistered application/java-vm that some servers send for .class. The magic is shared with the Mach-O universal binary header.",
        "specification": "Java Virtual Machine Specification, Java SE 21, §4.1 \"The ClassFile Structure\" — magic = 0xCAFEBABE",
        "specificationUrl": "https://docs.oracle.com/javase/specs/jvms/se21/html/jvms-4.html"
      },
      {
        "signatureId": "macho-fat-32",
        "formatId": "mach-o-universal",
        "formatName": "Mach-O universal (\"fat\") binary",
        "category": "executable",
        "offset": 0,
        "pattern": "CA FE BA BE",
        "matchedBytes": "CA FE BA BE",
        "bytesNeeded": 4,
        "literalBytes": 4,
        "mimeType": null,
        "extensions": [
          "dylib"
        ],
        "containerFormatId": null,
    …
    ]
  }
}
Base64 input from a stream reader
GET /api/file-signatures/detect?bytes=UklGRiQAAABXRUJQ&encoding=base64
{
  "data": {
    "bytesProvided": 12,
    "encoding": "base64",
    "hexPreview": "52 49 46 46 24 00 00 00 57 45 42 50",
    "matches": [
      {
        "signatureId": "webp-riff-form",
        "formatId": "webp",
        "formatName": "WebP",
        "category": "image",
        "offset": 0,
        "pattern": "52 49 46 46 ?? ?? ?? ?? 57 45 42 50",
        "matchedBytes": "52 49 46 46 24 00 00 00 57 45 42 50",
        "bytesNeeded": 12,
        "literalBytes": 8,
        "mimeType": "image/webp",
        "extensions": [
          "webp"
        ],
        "containerFormatId": "riff",
        "note": "The four wildcard bytes are the RIFF file size.",
        "specification": "WebP Container Specification, \"WebP File Header\" — \"RIFF\", file size, then \"WEBP\"",
        "specificationUrl": "https://developers.google.com/speed/webp/docs/riff_container"
      },
      {
        "signatureId": "riff-header",
        "formatId": "riff",
        "formatName": "Resource Interchange File Format (bare header)",
        "category": "data",
        "offset": 0,
        "pattern": "52 49 46 46",
        "matchedBytes": "52 49 46 46",
        "bytesNeeded": 4,
        "literalBytes": 4,
        "mimeType": null,
        "extensions": [],
        "containerFormatId": null,
        "note": "A generic container. The form type at offset 8 is what names the format — WAVE, AVI and WebP are separate rows here and outrank this one.",
        "specification": "Microsoft AVI RIFF File Reference, \"RIFF File Format\" — the RIFF header is the FOURCC \"RIFF\", a 4-byte file size, then a FOURCC form type",
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
A ZIP prefix, which cannot tell you DOCX from JAR
GET /api/file-signatures/detect?bytes=504b0304
{
  "data": {
    "bytesProvided": 4,
    "encoding": "hex",
    "hexPreview": "50 4B 03 04",
    "matches": [
      {
        "signatureId": "zip-local-file-header",
        "formatId": "zip",
        "formatName": "ZIP archive",
        "category": "archive",
        "offset": 0,
        "pattern": "50 4B 03 04",
        "matchedBytes": "50 4B 03 04",
        "bytesNeeded": 4,
        "literalBytes": 4,
        "mimeType": "application/zip",
        "extensions": [
          "zip"
        ],
        "containerFormatId": null,
        "note": "The first entry of a non-empty archive. DOCX, XLSX, PPTX, JAR, APK, ODF-in-name-only and ZIP-based installers all begin with exactly these four bytes, and none of their specifications fixes the entry order, so they cannot be told apart from a plain ZIP in the prefix. EPUB and OpenDocument are the only exceptions in this table because their specifications DO require an uncompressed \"mimetype\" first entry.",
        "specification": ".ZIP File Format Specification, APPNOTE 6.3.10 (2022-11-01), §4.3.7 \"Local file header\" — signature 0x04034b50",
        "specificationUrl": "https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT"
      }
    ],
    "matchCount": 1,
    "best": {
      "signatureId": "zip-local-file-header",
      "formatId": "zip",
      "formatName": "ZIP archive",
      "category": "archive",
      "offset": 0,
      "pattern": "50 4B 03 04",
      "matchedBytes": "50 4B 03 04",
      "bytesNeeded": 4,
      "literalBytes": 4,
      "mimeType": "application/zip",
      "extensions": [
        "zip""generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
A real DOCX: neither confirmed nor refuted, not accused
GET /api/file-signatures/detect?bytes=504b0304&claimedName=report.docx&claimedType=application%2Fvnd.openxmlformats-officedocument.wordprocessingml.document
{
  "data": {
    "bytesProvided": 4,
    "encoding": "hex",
    "hexPreview": "50 4B 03 04",
    "matches": [
      {
        "signatureId": "zip-local-file-header",
        "formatId": "zip",
        "formatName": "ZIP archive",
        "category": "archive",
        "offset": 0,
        "pattern": "50 4B 03 04",
        "matchedBytes": "50 4B 03 04",
        "bytesNeeded": 4,
        "literalBytes": 4,
        "mimeType": "application/zip",
        "extensions": [
          "zip"
        ],
        "containerFormatId": null,
        "note": "The first entry of a non-empty archive. DOCX, XLSX, PPTX, JAR, APK, ODF-in-name-only and ZIP-based installers all begin with exactly these four bytes, and none of their specifications fixes the entry order, so they cannot be told apart from a plain ZIP in the prefix. EPUB and OpenDocument are the only exceptions in this table because their specifications DO require an uncompressed \"mimetype\" first entry.",
        "specification": ".ZIP File Format Specification, APPNOTE 6.3.10 (2022-11-01), §4.3.7 \"Local file header\" — signature 0x04034b50",
        "specificationUrl": "https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT"
      }
    ],
    "matchCount": 1,
    "best": {
      "signatureId": "zip-local-file-header",
      "formatId": "zip",
      "formatName": "ZIP archive",
      "category": "archive",
      "offset": 0,
      "pattern": "50 4B 03 04",
      "matchedBytes": "50 4B 03 04",
      "bytesNeeded": 4,
      "literalBytes": 4,
      "mimeType": "application/zip",
      "extensions": [
        "zip""generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
A structured-suffix media type, with the '+' percent-encoded
GET /api/file-signatures/detect?bytes=504b0304&claimedType=application%2Fepub%2Bzip
{
  "data": {
    "bytesProvided": 4,
    "encoding": "hex",
    "hexPreview": "50 4B 03 04",
    "matches": [
      {
        "signatureId": "zip-local-file-header",
        "formatId": "zip",
        "formatName": "ZIP archive",
        "category": "archive",
        "offset": 0,
        "pattern": "50 4B 03 04",
        "matchedBytes": "50 4B 03 04",
        "bytesNeeded": 4,
        "literalBytes": 4,
        "mimeType": "application/zip",
        "extensions": [
          "zip"
        ],
        "containerFormatId": null,
        "note": "The first entry of a non-empty archive. DOCX, XLSX, PPTX, JAR, APK, ODF-in-name-only and ZIP-based installers all begin with exactly these four bytes, and none of their specifications fixes the entry order, so they cannot be told apart from a plain ZIP in the prefix. EPUB and OpenDocument are the only exceptions in this table because their specifications DO require an uncompressed \"mimetype\" first entry.",
        "specification": ".ZIP File Format Specification, APPNOTE 6.3.10 (2022-11-01), §4.3.7 \"Local file header\" — signature 0x04034b50",
        "specificationUrl": "https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT"
      }
    ],
    "matchCount": 1,
    "best": {
      "signatureId": "zip-local-file-header",
      "formatId": "zip",
      "formatName": "ZIP archive",
      "category": "archive",
      "offset": 0,
      "pattern": "50 4B 03 04",
      "matchedBytes": "50 4B 03 04",
      "bytesNeeded": 4,
      "literalBytes": 4,
      "mimeType": "application/zip",
      "extensions": [
        "zip""generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Four bytes of a WebP: the form type at byte 8 has not been read yet
GET /api/file-signatures/detect?bytes=52494646&claimedName=hero.webp&claimedType=image%2Fwebp
{
  "data": {
    "bytesProvided": 4,
    "encoding": "hex",
    "hexPreview": "52 49 46 46",
    "matches": [
      {
        "signatureId": "riff-header",
        "formatId": "riff",
        "formatName": "Resource Interchange File Format (bare header)",
        "category": "data",
        "offset": 0,
        "pattern": "52 49 46 46",
        "matchedBytes": "52 49 46 46",
        "bytesNeeded": 4,
        "literalBytes": 4,
        "mimeType": null,
        "extensions": [],
        "containerFormatId": null,
        "note": "A generic container. The form type at offset 8 is what names the format — WAVE, AVI and WebP are separate rows here and outrank this one.",
        "specification": "Microsoft AVI RIFF File Reference, \"RIFF File Format\" — the RIFF header is the FOURCC \"RIFF\", a 4-byte file size, then a FOURCC form type",
        "specificationUrl": "https://learn.microsoft.com/en-us/windows/win32/directshow/avi-riff-file-reference"
      }
    ],
    "matchCount": 1,
    "best": {
      "signatureId": "riff-header",
      "formatId": "riff",
      "formatName": "Resource Interchange File Format (bare header)",
      "category": "data",
      "offset": 0,
      "pattern": "52 49 46 46",
      "matchedBytes": "52 49 46 46",
      "bytesNeeded": 4,
      "literalBytes": 4,
      "mimeType": null,
      "extensions": [],
      "containerFormatId": null,
      "note": "A generic container. The form type at offset 8 is what names the format — WAVE, AVI and WebP are separate rows here and outrank this one.",
      "specification": "Microsoft AVI RIFF File Reference, \"RIFF File Format\" — the RIFF header is the FOURCC \"RIFF\", a 4-byte file size, then a FOURCC form type",
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}

GET /api/file-signatures/lookup

Provide exactly one of extension, mimeType or formatId. A key claimed by several formats resolves to the least refined of them — a standalone format before one that only exists inside another, so .jpg resolves to jpeg rather than to the Exif refinement of it — then table order, and a warning always names the other claimants. An unknown key is an honest 404 that says the format is not covered by this table — never a substituted record.

Live requestRuns against the public API
No key required
GET/api/file-signatures/lookup?extension=webp

Request parameters

Extension to look up (case-insensitive; a leading dot is fine: 'webp' or '.webp').

Media type to look up (case-insensitive; parameters are stripped).

This table's own format id, as returned by the list route (case-insensitive).

Advanced response options4 options

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

Return all fields except these (comma-separated).

Pretty-print the JSON response.

Drop the envelope: return the raw array/object without data/meta wrapper.

Response

Example parameters are ready. Send the request to inspect the live response.

Route reference

Code samples Ready-to-copy requests in 4 languages
Choose a code sample language
curl "https://randomapi.dev/api/file-signatures/lookup?extension=webp"
const res = await fetch("https://randomapi.dev/api/file-signatures/lookup?extension=webp");
const { data, meta } = await res.json();
import requests

data = requests.get("https://randomapi.dev/api/file-signatures/lookup?extension=webp").json()["data"]
$json = json_decode(file_get_contents(
  "https://randomapi.dev/api/file-signatures/lookup?extension=webp"
), true);
$data = $json["data"];
Parameters Route-specific request inputs 3
extension string

Extension to look up (case-insensitive; a leading dot is fine: 'webp' or '.webp').

allowed: 1 – 16
example: extension=webp
mimeType string

Media type to look up (case-insensitive; parameters are stripped).

allowed: 1 – 255
example: mimeType=application/pdf
formatId string

This table's own format id, as returned by the list route (case-insensitive).

allowed: 1 – 48
example: formatId=sqlite3
Universal parameters Shared response and formatting options 4
fields list

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

example: fields=formatId,formatName
exclude list

Return all fields except these (comma-separated).

example: exclude=note
pretty boolean

Pretty-print the JSON response.

default: false
example: pretty=true
unwrap boolean

Drop the envelope: return the raw array/object without data/meta wrapper.

default: false
example: unwrap=true
Response schema Fields returned in each record 9
formatId string

This table's stable id for the format.

example: webp

formatName string

The format's name as its own specification writes it.

example: WebP

category string

One of: image | audio | video | archive | compression | document | executable | font | database | disk-image | data.

example: image

extensions string[]

Conventional extensions (lowercase, no dot, sorted). Conventions, not guarantees.

example: ["webp"]

mimeType string nullable

The media type the format's own registration declares (or the single type it is universally served as), when that exact string exists in this API's IANA media-types dataset. null when no type identifies this format — see the list route's field description for the three cases.

example: image/webp

containerFormatId string nullable

The container this format lives inside, when it has one.

example: riff

signatures object[]

Every signature registered for this format, in table order, bounded by construction (the most any format has today is four, for Mach-O). Element keys: `signatureId`, `offset`, `pattern`, `bytesNeeded`, `literalBytes`, `trailer`, `note`, `specification`, `specificationUrl`, `sourceKind`.

example: [{"signatureId":"webp-riff-form","offset":0,"pattern":"52 49 46 46 ?? ?? ?? ?? 57 45 42 50","bytesNeeded":12,"literalBytes":8,"trailer":null,"note":"The four wildcard bytes are the RIFF file size.","specification":"WebP Container Specification, \"WebP File Header\" — \"RIFF\", file size, then \"WEBP\"","specificationUrl":"https://developers.google.com/speed/webp/docs/riff_container","sourceKind":"vendor-specification"}]

signatureCount integer

signatures.length.

example: 1

note string nullable

Format-level caveat: what this signature does not tell you. null when there is nothing honest to add.

example: null

Documented examples

Build-generated requests and complete responses
5
Every signature WebP registers
GET /api/file-signatures/lookup?extension=webp
{
  "data": {
    "formatId": "webp",
    "formatName": "WebP",
    "category": "image",
    "extensions": [
      "webp"
    ],
    "mimeType": "image/webp",
    "containerFormatId": "riff",
    "signatures": [
      {
        "signatureId": "webp-riff-form",
        "offset": 0,
        "pattern": "52 49 46 46 ?? ?? ?? ?? 57 45 42 50",
        "bytesNeeded": 12,
        "literalBytes": 8,
        "trailer": null,
        "note": "The four wildcard bytes are the RIFF file size.",
        "specification": "WebP Container Specification, \"WebP File Header\" — \"RIFF\", file size, then \"WEBP\"",
        "specificationUrl": "https://developers.google.com/speed/webp/docs/riff_container",
        "sourceKind": "vendor-specification"
      }
    ],
    "signatureCount": 1,
    "note": null
  },
  "meta": {
    "endpoint": "file-signatures",
    "route": "/lookup",
    "params": {
      "extension": "webp"
    },
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Look up by media type
GET /api/file-signatures/lookup?mimeType=application/pdf
{
  "data": {
    "formatId": "pdf",
    "formatName": "Portable Document Format",
    "category": "document",
    "extensions": [
      "pdf"
    ],
    "mimeType": "application/pdf",
    "containerFormatId": null,
    "signatures": [
      {
        "signatureId": "pdf-header",
        "offset": 0,
        "pattern": "25 50 44 46 2D",
        "bytesNeeded": 5,
        "literalBytes": 5,
        "trailer": "25 25 45 4F 46",
        "note": "The version digits after %PDF- are not part of this pattern. The %%EOF trailer is recorded but never checked: /detect sees a prefix, not a whole file.",
        "specification": "ISO 32000-2:2020 (PDF 2.0), §7.5.2 \"File header\" — the first line is %PDF-n.m; §7.5.5 \"File trailer\" — the last line of the file is %%EOF",
        "specificationUrl": "https://pdfa.org/sponsored-standards/",
        "sourceKind": "standard"
      }
    ],
    "signatureCount": 1,
    "note": "The version digits after %PDF- are not part of this pattern. The %%EOF trailer is recorded but never checked: /detect sees a prefix, not a whole file."
  },
  "meta": {
    "endpoint": "file-signatures",
    "route": "/lookup",
    "params": {
      "mimeType": "application/pdf"
    },
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Look up by this table's format id
GET /api/file-signatures/lookup?formatId=isobmff
{
  "data": {
    "formatId": "isobmff",
    "formatName": "ISO base media file format (bare ftyp box)",
    "category": "video",
    "extensions": [],
    "mimeType": null,
    "containerFormatId": null,
    "signatures": [
      {
        "signatureId": "isobmff-ftyp",
        "offset": 4,
        "pattern": "66 74 79 70",
        "bytesNeeded": 8,
        "literalBytes": 4,
        "trailer": null,
        "note": "This row means only \"an ISO base media file\". The 4-byte major brand at offset 8 is what names the format, and every brand this table covers is a separate row that outranks this one.",
        "specification": "ISO/IEC 14496-12, \"File Type Box\" — the ftyp box type occupies bytes 4–7 of the file and is followed by the major brand",
        "specificationUrl": null,
        "sourceKind": "standard"
      }
    ],
    "signatureCount": 1,
    "note": "This row means only \"an ISO base media file\". The 4-byte major brand at offset 8 is what names the format, and every brand this table covers is a separate row that outranks this one."
  },
  "meta": {
    "endpoint": "file-signatures",
    "route": "/lookup",
    "params": {
      "formatId": "isobmff"
    },
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
A format with four signatures
GET /api/file-signatures/lookup?formatId=mach-o
{
  "data": {
    "formatId": "mach-o",
    "formatName": "Mach-O object/executable",
    "category": "executable",
    "extensions": [
      "dylib",
      "o"
    ],
    "mimeType": null,
    "containerFormatId": null,
    "signatures": [
      {
        "signatureId": "macho-cigam-32",
        "offset": 0,
        "pattern": "CE FA ED FE",
        "bytesNeeded": 4,
        "literalBytes": 4,
        "trailer": null,
        "note": "MH_CIGAM — 32-bit, byte-swapped relative to the host (little-endian on disk). Four magics: two byte orders times 32- and 64-bit. Mach-O has no registered media type.",
        "specification": "Apple xnu, EXTERNAL_HEADERS/mach-o/loader.h, the \"mach_header\" / \"mach_header_64\" magic constants — MH_MAGIC 0xfeedface, MH_CIGAM 0xcefaedfe, MH_MAGIC_64 0xfeedfacf, MH_CIGAM_64 0xcffaedfe (Mach-O has no published specification)",
        "specificationUrl": "https://github.com/apple-oss-distributions/xnu/blob/main/EXTERNAL_HEADERS/mach-o/loader.h",
        "sourceKind": "reference-implementation"
      },
      {
        "signatureId": "macho-cigam-64",
        "offset": 0,
        "pattern": "CF FA ED FE",
        "bytesNeeded": 4,
        "literalBytes": 4,
        "trailer": null,
        "note": "MH_CIGAM_64 — 64-bit little-endian on disk, which is what current macOS binaries look like. Four magics: two byte orders times 32- and 64-bit. Mach-O has no registered media type.",
        "specification": "Apple xnu, EXTERNAL_HEADERS/mach-o/loader.h, the \"mach_header\" / \"mach_header_64\" magic constants — MH_MAGIC 0xfeedface, MH_CIGAM 0xcefaedfe, MH_MAGIC_64 0xfeedfacf, MH_CIGAM_64 0xcffaedfe (Mach-O has no published specification)",
        "specificationUrl": "https://github.com/apple-oss-distributions/xnu/blob/main/EXTERNAL_HEADERS/mach-o/loader.h",
        "sourceKind": "reference-implementation"
      },
      {
        "signatureId": "macho-magic-32",
        "offset": 0,
        "pattern": "FE ED FA CE",
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
An extension two formats claim: .jpg resolves to the base format
GET /api/file-signatures/lookup?extension=jpg
{
  "data": {
    "formatId": "jpeg",
    "formatName": "JPEG (JFIF/Exif-agnostic baseline marker sequence)",
    "category": "image",
    "extensions": [
      "jpe",
      "jpeg",
      "jpg"
    ],
    "mimeType": "image/jpeg",
    "containerFormatId": null,
    "signatures": [
      {
        "signatureId": "jpeg-soi",
        "offset": 0,
        "pattern": "FF D8 FF",
        "bytesNeeded": 3,
        "literalBytes": 3,
        "trailer": null,
        "note": "The third byte is the X'FF' that begins the marker after SOI, which the marker syntax requires; the marker itself is not fixed, so this row deliberately claims only three bytes.",
        "specification": "ITU-T T.81 (09/1992), also published as ISO/IEC 10918-1, Annex B \"Compressed data formats\", \"Marker assignments\" (Table B.1) — SOI = X'FFD8', and every marker is X'FF' followed by a byte that is neither X'00' nor X'FF'",
        "specificationUrl": "https://www.w3.org/Graphics/JPEG/itu-t81.pdf",
        "sourceKind": "standard"
      }
    ],
    "signatureCount": 1,
    "note": "The third byte is the X'FF' that begins the marker after SOI, which the marker syntax requires; the marker itself is not fixed, so this row deliberately claims only three bytes."
  },
  "meta": {
    "endpoint": "file-signatures",
    "route": "/lookup",
    "params": {
      "extension": "jpg"
    },
    "generatedAt": "2026-07-30T15:14:08.000Z",
    "warnings": [
      "'.jpg' is claimed by 2 formats in this table — it resolves to the least refined of them, 'jpeg'. Also claiming it: exif-jpeg."
    ]
  }
}

GET /api/file-signatures/prefix-length

Answers the question every upload tutorial skips: 'read the first 16 bytes' decides some of this table and not the rest, and this route says exactly which. All figures are counted from the shipped table, never hand-written.

Live requestRuns against the public API
No key required
GET/api/file-signatures/prefix-length

Request parameters

How many leading bytes your validator reads. The default is 16 because 'read the first 16 bytes' is the convention every upload tutorial repeats — this route says what that actually buys you.

Restrict the answer to one category.

Advanced response options4 options

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

Return all fields except these (comma-separated).

Pretty-print the JSON response.

Drop the envelope: return the raw array/object without data/meta wrapper.

Response

Example parameters are ready. Send the request to inspect the live response.

Route reference

Code samples Ready-to-copy requests in 4 languages
Choose a code sample language
curl "https://randomapi.dev/api/file-signatures/prefix-length"
const res = await fetch("https://randomapi.dev/api/file-signatures/prefix-length");
const { data, meta } = await res.json();
import requests

data = requests.get("https://randomapi.dev/api/file-signatures/prefix-length").json()["data"]
$json = json_decode(file_get_contents(
  "https://randomapi.dev/api/file-signatures/prefix-length"
), true);
$data = $json["data"];
Parameters Route-specific request inputs 2
bytes int

How many leading bytes your validator reads. The default is 16 because 'read the first 16 bytes' is the convention every upload tutorial repeats — this route says what that actually buys you.

default: 16
allowed: 1 – 65536
example: bytes=12
category enum

Restrict the answer to one category.

default: any
allowed: image | audio | video | archive | compression | document | executable | font | database | disk-image | data | any
example: category=image
Universal parameters Shared response and formatting options 4
fields list

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

example: fields=category,bytesRequested
exclude list

Return all fields except these (comma-separated).

example: exclude=beyondRequest
pretty boolean

Pretty-print the JSON response.

default: false
example: pretty=true
unwrap boolean

Drop the envelope: return the raw array/object without data/meta wrapper.

default: false
example: unwrap=true
Response schema Fields returned in each record 9
category string

Resolved `category`.

example: any

bytesRequested integer

Resolved `bytes` (echoed here as well as in meta.params).

example: 16

signatureCount integer

Signatures in scope.

example: 116

formatCount integer

Distinct formats in scope.

example: 99

signaturesDecidable integer

Signatures whose bytesNeeded ≤ bytesRequested.

example: 108

formatsDecidable integer

Formats with at least one decidable signature.

example: 91

maxBytesNeeded integer

Largest bytesNeeded in scope — the ceiling that decides everything.

example: 32774

thresholds object[]

Fixed, documented cut-offs (4, 8, 12, 16, 32, 64, 128, 264, 512, 1024, then maxBytesNeeded if not already listed), each `{ bytes, signaturesDecidable, formatsDecidable }`. Cut-offs above maxBytesNeeded are omitted.

example: [{"bytes":4,"signaturesDecidable":62,"formatsDecidable":55}]

beyondRequest object[]

Up to 10 signatures that need more than bytesRequested, ascending by bytesNeeded, each `{ signatureId, formatName, bytesNeeded }` — so the number is never mysterious. A warning names how many more exist.

example: [{"signatureId":"tar-ustar","formatName":"POSIX ustar archive","bytesNeeded":265}]

Documented examples

Build-generated requests and complete responses
3
What the conventional first 16 bytes actually decide
GET /api/file-signatures/prefix-length
{
  "data": {
    "category": "any",
    "bytesRequested": 16,
    "signatureCount": 116,
    "formatCount": 99,
    "signaturesDecidable": 102,
    "formatsDecidable": 85,
    "maxBytesNeeded": 32774,
    "thresholds": [
      {
        "bytes": 4,
        "signaturesDecidable": 48,
        "formatsDecidable": 40
      },
      {
        "bytes": 8,
        "signaturesDecidable": 71,
        "formatsDecidable": 61
      },
      {
        "bytes": 12,
        "signaturesDecidable": 100,
        "formatsDecidable": 83
      },
      {
        "bytes": 16,
        "signaturesDecidable": 102,
        "formatsDecidable": 85
      },
      {
        "bytes": 32,
        "signaturesDecidable": 104,
        "formatsDecidable": 87
      },
      {
        "bytes": 64,
        "signaturesDecidable": 108,
        "formatsDecidable": 91
      },
    …
    ]
  }
}
Twelve bytes, images only
GET /api/file-signatures/prefix-length?bytes=12&category=image
{
  "data": {
    "category": "image",
    "bytesRequested": 12,
    "signatureCount": 23,
    "formatCount": 19,
    "signaturesDecidable": 23,
    "formatsDecidable": 19,
    "maxBytesNeeded": 12,
    "thresholds": [
      {
        "bytes": 4,
        "signaturesDecidable": 7,
        "formatsDecidable": 6
      },
      {
        "bytes": 8,
        "signaturesDecidable": 12,
        "formatsDecidable": 10
      },
      {
        "bytes": 12,
        "signaturesDecidable": 23,
        "formatsDecidable": 19
      }
    ],
    "beyondRequest": []
  },
  "meta": {
    "endpoint": "file-signatures",
    "route": "/prefix-length",
    "params": {
      "bytes": 12,
      "category": "image"
    },
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
The whole table's ceiling
GET /api/file-signatures/prefix-length?bytes=65536
{
  "data": {
    "category": "any",
    "bytesRequested": 65536,
    "signatureCount": 116,
    "formatCount": 99,
    "signaturesDecidable": 116,
    "formatsDecidable": 99,
    "maxBytesNeeded": 32774,
    "thresholds": [
      {
        "bytes": 4,
        "signaturesDecidable": 48,
        "formatsDecidable": 40
      },
      {
        "bytes": 8,
        "signaturesDecidable": 71,
        "formatsDecidable": 61
      },
      {
        "bytes": 12,
        "signaturesDecidable": 100,
        "formatsDecidable": 83
      },
      {
        "bytes": 16,
        "signaturesDecidable": 102,
        "formatsDecidable": 85
      },
      {
        "bytes": 32,
        "signaturesDecidable": 104,
        "formatsDecidable": 87
      },
      {
        "bytes": 64,
        "signaturesDecidable": 108,
        "formatsDecidable": 91
      },
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}

About this API

Coverage & behavior

116 byte patterns across 99 file formats, every one of them read from the format's own published specification and cited to the document and clause it came from — 84 distinct citations, verified 2026-07-30. Nothing here comes from an aggregated signature list, an npm package or memory. If a clause could not be cited, the row was left out.

Four routes:

  • /api/file-signatures — the whole table, filterable by category, search, extension, maxOffsetBytes and containers.
  • /api/file-signatures/detect?bytes=… — paste the first bytes you read off a stream (hex or base64) and get every signature they satisfy, ranked. Add claimedName and claimedType and it also tells you whether an upload's filename and Content-Type agree with its bytes.
  • /api/file-signatures/lookup?extension=… — one format and all of its signatures, by extension, media type or format id.
  • /api/file-signatures/prefix-length?bytes=16 — how many leading bytes a streaming validator must read, and exactly what that buys you.

The ranking rule

/detect returns a ranked list, not a verdict. The order is a total order: literalBytes descending, then offset ascending, then signatureId ascending — no randomness, no scoring heuristic, no tie left to array order. A refinement therefore naturally outranks its container (ftyp + brand avif is 8 literal bytes and beats the bare ftyp row's 4) and both are returned. best is documented as "the match with the most literal evidence", which is not the same thing as "what this file is".

Three answers, not two: how a claim is judged

claimedName and claimedType are checked against the bytes, and extensionAgrees/typeAgrees are deliberately three-valued. A claim is only false when the bytes refute it: every signature of every format in this table that claims that extension or media type has a literal byte, inside the prefix you supplied, that contradicts it. PNG bytes labelled .jpg/image/jpeg are refuted, because byte 0 of all three JPEG rows disagrees — so that is false, and verdict is "disagrees".

Two situations produce null instead, and both used to be the easy place for a tool like this to lie:

  • This table has no signature for what you claimed. A genuine DOCX, JAR, APK, WebM, WMA or camera raw is byte-identical to its container in the prefix, so application/…wordprocessingml.document can be neither confirmed nor refuted. You get null and a reason naming what the bytes do match — never an accusation the endpoint has no basis for.
  • The deciding bytes are past your prefix. Four bytes of a WebP are just the RIFF header; the form type that decides .webp sits at byte 8. Those four bytes do not contradict the claim, so the answer is null with the byte count the signature needs — the same honesty notEvaluated gives the match list.

That is why the useful pattern for an upload validator is "reject on disagrees, read more bytes on undetermined", not "reject on anything that is not agrees".

What this endpoint will not claim

  1. It is not a safety check. A match means the header is consistent with a format. It is not validity, not a parse, not a schema check and never a malware scan. There is no safe, valid or trusted field, and there never will be.
  2. It is not exclusive. Polyglot files exist, so /detect returns a list.
  3. Prefix-only, from offset 0. There is deliberately no offset/skip parameter: a caller-asserted offset base cannot be verified, and getting it wrong produces a confidently wrong answer. Rows whose bytesNeeded exceeds what you supplied are counted in notEvaluated, never silently treated as "no match". ISO 9660's identifier sits at byte 32 769 and is therefore always unevaluable through /detect — that is stated rather than hidden, because it is what makes /prefix-length honest.
  4. Trailers are recorded, never checked. trailer exists because upload validators ask for it. A prefix cannot see the end of a file.
  5. ZIP-based formats are not guessed. DOCX/XLSX/PPTX (ECMA-376 OPC), JAR, APK and ZIP-based installers are indistinguishable from a plain ZIP in the prefix, because their specifications do not fix the entry order. /detect returns zip and says so — and a DOCX or JAR claimedType comes back as typeAgrees: null with a reason, not as a mismatch, because these bytes cannot refute it. EPUB and OpenDocument are the only exceptions, because OCF §4.3 and ODF Part 2 §3.3 require an uncompressed mimetype first entry — for those, the bytes really are a signature.
  6. Office binary formats are not guessed. .doc, .xls, .ppt, .msg and .msi all share the Compound File Binary header; the distinguishing streams live in the directory, which a prefix does not reach.
  7. TIFF-based raw formats are not distinguished. CR2/NEF/ORF/ARW are TIFF containers whose distinguishing structures are vendor-specific and mostly unpublished.
  8. Matroska vs WebM is not decided. Both start with the EBML magic; the DocType element decides, and reading it means parsing EBML. A video/webm claim therefore comes back null, not false. The same applies to ASF: whether the file is WMV or WMA lives in the stream properties objects, past the prefix.
  9. Formats with no magic number are out of scope, by name: plain text, CSV, TSV, JSON, JavaScript, CSS, HTML (<!DOCTYPE is neither required nor case-fixed — the HTML spec matches it ASCII-case-insensitively, so unlike XML's <?xml, which XML 1.0 §2.8 spells exactly, it is not a fixed byte sequence at all), Brotli (RFC 7932 defines none), raw DEFLATE, zlib streams (RFC 1950's CMF/FLG is a checksummed pair, not a constant), raw MPEG audio frames (the FF Ex sync word is a frame marker, not a file signature) and .pyc (the magic changes with every CPython release). Each absence is a documented decision, not an oversight.
  10. Skippable frames are excluded. Zstandard's (0x184D2A50–0x184D2A5F, RFC 8878 §3.1.2) and LZ4's equivalent vary in the low nibble, which a per-byte ?? wildcard cannot express without claiming all 256 first bytes.
  11. Extensions are conventions. extensions describes what people name these files. This endpoint exists precisely because that can lie.
  12. Nothing is fetched. We never open your file, your URL or your origin — you paste the bytes you already read, and they are not stored.

Use it for

  • Reject an upload whose bytes genuinely refute its filename extension or Content-Type, without failing legitimate ZIP- and MP4-based formats
  • Identify a file from the first bytes of a stream without buffering the whole body
  • Decide how many leading bytes your streaming validator has to read before it can classify anything
  • Explain to a user why a .docx and a .jar are indistinguishable from a plain ZIP in the first bytes
  • Look up the exact specification clause that defines a format's magic number for a code review or audit

Frequently asked questions

How do I check a file's real type instead of trusting its extension?

/detect takes the first bytes you read off the stream (hex or base64) and returns every signature those bytes satisfy, ranked by how many literal bytes matched, with the canonical media type. Pass claimedName and claimedType and it also tells you whether the upload's filename and Content-Type agree with the bytes.

Can magic bytes prove a file is safe or valid?

No. A match means the header is consistent with a format. It is not a validity check, not a parse, and not a malware scan; a polyglot file can satisfy two signatures at once, which is why /detect returns a ranked list instead of one winner.

How do I tell a DOCX, JAR or APK apart from a plain ZIP?

You cannot, from the first bytes: all of them begin with the same ZIP local-file-header signature, and none of their specifications fixes the entry order. /detect says so instead of guessing — pass claimedType=application/vnd.openxmlformats-officedocument.wordprocessingml.document and it reports typeAgrees: null, not a mismatch. EPUB and OpenDocument are the exceptions: their own specs require an uncompressed mimetype entry first, so those bytes really are a signature.

Why does /detect say 'undetermined' instead of rejecting the upload?

Because extensionAgrees and typeAgrees are only false when the bytes genuinely refute the claim — every signature for that extension or media type contradicts a byte you supplied. null means this table has no signature for what you claimed (a real DOCX or WebM), or the deciding bytes sit past your prefix (four bytes of a WebP are just the RIFF header). Reject on disagrees; read more bytes on undetermined.

How many bytes do I need to read to identify a file?

/prefix-length answers it from the table: state how many bytes you can afford and it reports how many signatures and formats that decides, which ones need more (POSIX ustar sits at offset 257; ISO 9660's CD001 at 32769) and the exact ceiling for the whole table, which is 32774 bytes.

Standards & references