FormData vs JSON: When to Use Each for API Requests

Guides6 min read
FormData vs JSON: When to Use Each for APIs - dark green geometric blog banner

When you call an API from the browser, the body format is part of the contract. FormData vs JSON is one of the first choices that confuses people building forms and SPAs: JSON for REST APIs, FormData for uploads and HTML-shaped posts, and classic form encoding when there is no JavaScript at all. This guide is a practical reference for when to use each, how Content-Type works, and how to debug mismatches.

What is the difference between FormData and JSON?

JSON FormData / form encoding
Wire format Text (JSON.stringify) Multipart parts or urlencoded key/value pairs
Typical header Content-Type: application/json Browser sets multipart/form-data; boundary=... for FormData, or application/x-www-form-urlencoded for classic forms
Nested data Objects and arrays naturally Flat fields (nesting needs naming tricks)
Files Poor fit (base64 or separate API) First-class with File / Blob
HTML <form> default No Yes

JSON is a data interchange format. FormData is a browser API that builds a multipart/form-data body from fields and files. Related but separate: a plain HTML form without JS often sends application/x-www-form-urlencoded instead of multipart.

When to use JSON for API requests

Use JSON when the server expects a structured document:

const payload = {
  name: 'Alex',
  email: 'alex@example.com',
  tags: ['urgent', 'sales'],
  meta: { source: 'web' },
};

await fetch('https://api.example.com/leads', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Accept: 'application/json',
  },
  body: JSON.stringify(payload),
});

Do not use JSON as the wire format when you only have a progressive HTML form with no JS, or when the endpoint only parses multipart/urlencoded bodies.

When to use FormData for API requests

Use FormData when you need form-shaped or file-shaped traffic:

const form = document.querySelector('#contact');
const data = new FormData(form);
// data.append('name', 'Alex');
// data.append('file', fileInput.files[0]);

await fetch('https://api.example.com/upload', {
  method: 'POST',
  body: data,
  // Do not set Content-Type manually. The browser adds the multipart boundary.
});

Do not use FormData when the API only accepts JSON schemas, or when you need deep nested structures without painful field names.

When to use a plain HTML form (no FormData API)

If the page can work without JavaScript, prefer a normal form POST. The browser encodes fields for you:

<form action="https://api.example.com/submit" method="POST">
  <input type="text" name="name" required />
  <input type="email" name="email" required />
  <textarea name="message" required></textarea>
  <button type="submit">Submit</button>
</form>

This is still “form encoding,” not JSON. Many contact and marketing endpoints are built for this shape.

FormData vs URLSearchParams vs JSON (quick map)

Tool Typical use
JSON.stringify + application/json App APIs, nested data
FormData Multipart, files + fields
URLSearchParams Urlencoded bodies or query strings without files
Native <form method="POST"> No-JS submit, progressive enhancement

Example urlencoded body with URLSearchParams (no files):

const body = new URLSearchParams({
  name: 'Alex',
  email: 'alex@example.com',
});

await fetch('https://api.example.com/submit', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body,
});

Common Content-Type mistakes

  1. Setting Content-Type: multipart/form-data yourself
    You omit the boundary. Let the browser set the header when body is FormData.

  2. Passing a plain object as fetch body for JSON
    Use JSON.stringify. Do not assume every runtime serializes objects for you.

  3. JSON.stringify on a FormData instance
    Produces junk text, not multipart parts.

  4. JSON client against a form-only server (or the reverse)
    Empty body on the server is usually an encoding mismatch, not “wrong field names only.”

  5. CORS surprises after switching to JSON
    application/json often triggers a preflight. Simple form posts may not. Check preflight responses when debugging.

Example: read a form with FormData, send JSON to your API

In SPAs you often collect inputs from a form, then talk to a JSON API:

async function onSubmit(event) {
  event.preventDefault();
  const form = event.currentTarget;
  const payload = Object.fromEntries(new FormData(form).entries());

  await fetch('/api/leads', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload),
  });
}

Here FormData only reads fields. The network body is still JSON because that is what /api/leads expects.

Example: keep form encoding end to end

When the receiver is a form endpoint, keep form encoding on the wire. Build FormData (or use a native form POST) and do not force application/json.

async function onSubmit(event) {
  event.preventDefault();
  const data = new FormData(event.currentTarget);

  await fetch('https://api.example.com/form-endpoint', {
    method: 'POST',
    body: data,
  });
}

For contact forms on static sites and marketing pages, the no-JS HTML action + method="POST" pattern is often enough. See contact form on a static website without a backend if you want that path without standing up your own API.

Decision checklist: FormData or JSON?

  1. API docs say JSON?JSON.stringify + application/json.
  2. Uploading files in this request?FormData (multipart), unless there is a separate upload protocol.
  3. Must work with no JavaScript? → Native HTML form POST, not a JSON-only client.
  4. Endpoint is a form backend? → Form fields (urlencoded or multipart), not a JSON blob.
  5. Need nested objects and arrays? → JSON.

FAQ

Is FormData the same as multipart/form-data?

Almost always in browsers: new FormData() produces a multipart body and the correct boundary. Classic forms without files often use urlencoded instead.

Can I send JSON from an HTML form without JavaScript?

Not as a real application/json body. Without JS, the browser sends form encoding. To send JSON you need script (or a server that accepts form fields).

Should I always prefer JSON in modern apps?

No. Prefer whatever the endpoint documents. Modern apps still use multipart for uploads and form posts for progressive HTML.

Why is my server body empty?

Open DevTools → Network → the request payload. Confirm JSON text vs form fields, then match the server parser (JSON vs multipart vs urlencoded).

Does Object.fromEntries(new FormData(form)) support multiple values for one name?

Object.fromEntries keeps only the last value per key. For multi-selects or repeated names, iterate formData.getAll(name) instead.

Encoding is a contract, not a style choice

FormData vs JSON is about matching the client body to the server parser: nested app APIs lean JSON; files and HTML forms lean FormData or native form encoding. Get the Content-Type and body shape right before you debug business logic.

When the job is a website contact form and you do not want to own a JSON lead API plus mail infrastructure, a form-shaped endpoint is the natural fit: post fields as a form, filter spam on the server, and deliver notifications without a custom backend. FormsReach is built for that path (email and free webhooks on Free with 500 credits/month; WhatsApp via Meta’s Business API on Pro and Agency). For a full form-to-notification walkthrough, see send form submissions to WhatsApp.

Get your free API key →

#form-api#no-backend#react