← Blog

Data MIME Types Explained: JSON, Binary, Custom, and Everything In Between

"Data MIME" is how most developers casually refer to the MIME types used to identify data payloads β€” JSON, binary blobs, protocol buffers, form data, streams, and the application-specific formats that apps use to round-trip their state across copy boundaries, fetch calls, and file uploads. Unlike image or video MIME types, which describe the bytes themselves, data MIME types describe how to interpret the bytes as structured information.

This article covers the data MIME types you will meet in three places: HTTP request and response bodies, FormData uploads, and the clipboard. It explains what each type means, when to use it, the common mistakes, and how to inspect real-world payloads.

The canonical data MIME types

Five types cover ninety percent of the data you will send or receive in a web app:

  • application/json β€” structured data, human-readable, the default for REST and most SPAs
  • application/octet-stream β€” "raw bytes, figure it out yourself"
  • application/x-www-form-urlencoded β€” HTML form data, URL-encoded key/value pairs
  • multipart/form-data β€” HTML form with file uploads or mixed content
  • text/plain β€” unstructured string data

Each has different encoding rules, different size implications, and different debugger behaviour. Mixing them up is the most common source of "why is my backend receiving a stringified JSON blob?" bugs.

application/json

The workhorse. A Content-Type: application/json request means the body is a UTF-8 encoded JSON document. Every modern HTTP client serialises JavaScript objects automatically when you set this header. Every modern server parses it.

Common mistakes:

  • Sending JSON with no Content-Type header. The body is valid JSON but the server treats it as a blob and calls JSON.parse on [object Object] or worse. Set the header.
  • Sending JSON with application/x-www-form-urlencoded. Body gets URL-encoded into {"name":"Alice"}, server tries to parse it as a form, fails silently.
  • BOM at the start. Some tools prefix the JSON with a byte-order mark. JSON.parse throws SyntaxError on the \uFEFF.
  • Non-UTF-8 encoding. JSON is always UTF-8 by spec. If your server emits windows-1252, clients will accept bytes that are not valid UTF-8 and produce mojibake.

application/octet-stream

The generic "I don't know what this is, or I don't want the browser to guess" content type. Servers use it to force a download; browsers use it as a fallback when sniffing fails. If you see it in a clipboard paste or a file upload, it almost always means the source stripped or lost the real MIME type.

When to send it from your own code:

  • Uploading a blob of bytes that has no meaningful internet media type (sensor data, custom binary protocol)
  • Forcing a download of any file β€” browsers will not render application/octet-stream inline

When not to send it:

  • Uploading images, PDFs, or other well-known formats β€” use their real MIME types so caching, thumbnailing, and client-side validation work

application/x-www-form-urlencoded

The default <form> encoding when no enctype is set. The body looks like a query string: name=Alice&age=42&tags=one&tags=two. Compact, widely supported, and the right choice for simple key/value submissions.

Caveats:

  • Values are URL-encoded, so Unicode strings get percent-encoded. Expect a 3Γ— size inflation for non-ASCII text.
  • Nested objects and arrays require convention (brackets, dot-paths, repeated keys). Every framework picks a different one; document yours.
  • Not suitable for files. Use multipart/form-data instead.

multipart/form-data

Designed for forms with file uploads. The body is a sequence of "parts", each with its own Content-Type and Content-Disposition header. Parts are separated by a boundary string that must not appear in any part's content (browsers generate random boundaries to avoid collisions).

Content-Type: multipart/form-data; boundary=---XXXX

---XXXX
Content-Disposition: form-data; name="avatar"; filename="me.png"
Content-Type: image/png

<raw PNG bytes>
---XXXX
Content-Disposition: form-data; name="username"

alice
---XXXX--

From JavaScript, construct it with FormData:

const fd = new FormData()
fd.append('avatar', fileInput.files[0])
fd.append('username', 'alice')
await fetch('/upload', { method: 'POST', body: fd })

Do not set Content-Type yourself when sending FormData β€” the browser must set it so the boundary is correct.

text/plain and text/* subtypes

Use text/plain for any UTF-8 string payload that does not need parsing. Other text/* types worth knowing:

  • text/csv β€” comma-separated values, parseable by spreadsheet tools
  • text/tab-separated-values β€” TSV, what Excel produces when you copy
  • text/html β€” HTML content (common on the clipboard, rare as a data payload)
  • text/xml β€” XML (prefer application/xml for new code)
  • text/event-stream β€” Server-Sent Events, streaming text frames

Streaming and chunked data

  • text/event-stream β€” SSE. Each "event" is a set of field: value lines followed by a blank line. Used for real-time one-way updates from server to browser.
  • application/ndjson β€” newline-delimited JSON. Each line is a complete JSON document. Simple to parse with a line reader, friendly to generator functions.
  • application/jsonl / application/x-jsonlines β€” same thing under other names.
  • application/grpc β€” gRPC over HTTP/2. Binary-framed protobuf.

Custom application/* MIME types

When you need to identify a format that no registered MIME type describes, you have three conventions:

  • application/vnd.<vendor>.<format> β€” vendor-tree. Registered with IANA. Use this if you want the type to be globally recognised.
  • application/x-<format> β€” unregistered "experimental" prefix. Common but discouraged by modern RFCs; still works everywhere.
  • application/prs.<producer>.<format> β€” personal tree. Rare.

Example custom types you will see in the wild:

  • application/vnd.api+json β€” JSON:API spec payloads
  • application/ld+json β€” JSON-LD, used for structured data
  • application/x-vnd.google-docs-document-slice-clip+wrapped β€” Google Docs clipboard format
  • application/vnd.figma β€” Figma copy/paste format
  • application/x-protobuf β€” Protocol Buffers

If you are inventing one for your own app, prefer the vendor tree and pick a real reverse-DNS identifier. application/vnd.myapp.v1+json is well-structured: the +json suffix tells generic tools they can still parse the payload as JSON. For the full clipboard angle, see Writing Custom application/* MIME Types to the Clipboard.

Data MIME types on the clipboard

The clipboard is the least-discussed place where data MIME types matter. A copy from any app can place structured data alongside plain text, and a sufficiently clever paste handler can consume it directly:

document.addEventListener('paste', async (e) => {
  // Check for structured JSON first
  const json = e.clipboardData.getData('application/json')
  if (json) {
    try {
      const data = JSON.parse(json)
      return handleStructuredPaste(data)
    } catch {}
  }
  // Fall back to plain text
  const text = e.clipboardData.getData('text/plain')
  handleTextPaste(text)
})

To see which data MIME types a specific source actually writes, paste into Clipboard Inspector. Google Docs, Notion, Figma, and Office all publish richer types than you would guess from the surface UI.

Choosing a data MIME type: a decision tree

  • Is it JavaScript-object-shaped? β†’ application/json
  • Is it a structured form, no files? β†’ application/x-www-form-urlencoded
  • Does it include files? β†’ multipart/form-data
  • Is it raw bytes with no standard format? β†’ application/octet-stream
  • Is it a stream of records? β†’ application/ndjson or text/event-stream
  • Is it a known format with a registered MIME? β†’ Use the registered type. Look it up at iana.org/assignments/media-types before inventing one.
  • Is it your own format? β†’ application/vnd.yourapp.format+json if JSON-compatible, otherwise application/vnd.yourapp.format.

Debugging data MIME type issues

  • Server receives [object Object]: client sent an object where the HTTP library expected a string. Serialise with JSON.stringify and set Content-Type: application/json.
  • Browser downloads instead of rendering: server returned application/octet-stream or attachment Content-Disposition. Change the MIME type on the server.
  • CORS error only on JSON: application/json triggers a preflight. Your server's CORS config must allow the Content-Type header and OPTIONS method.
  • FormData arrives empty: you set Content-Type manually and broke the boundary. Remove the header and let the browser set it.

Related: Clipboard MIME Types Explained, Writing Custom application/* MIME Types, File Types & MIME Types Reference.