spyderproxy

What Is HTTP 415 Unsupported Media Type?

D

Daniel K.

|
Published date

Mon Apr 27 2026

Quick verdict: HTTP 415 (Unsupported Media Type) means the server rejected your request because the Content-Type header didn't match what the endpoint accepts. Most common causes: posting JSON without Content-Type: application/json, uploading a file in a format the server doesn't allow, or having a proxy or WAF strip the Content-Type. Fix the request — don't retry with backoff.

This guide covers what 415 means at the protocol level, the six causes that produce 99% of 415s in API and scraping workflows, copy-paste fixes for the four most common HTTP libraries (curl, Python requests, axios, fetch), and how 415 differs from 400 and 422.

What 415 Means in Plain Terms

RFC 9110 defines 415 as: "the origin server is refusing to service the request because the content is in a format not supported by this method on the target resource." In practice, this means:

  • You sent a request with a body (POST, PUT, PATCH).
  • The server understood the request well enough to look at the Content-Type header.
  • The Content-Type doesn't match what the endpoint expects.
  • The server returned 415 instead of trying to parse the body.

Two important properties: 415 is a permanent client error (retrying with the same headers will fail every time), and it happens before body parsing (so the server hasn't seen your actual data yet).

The 6 Most Common Causes

  1. Missing Content-Type header. Some libraries default to application/x-www-form-urlencoded when none is set; if the API wants JSON, you get 415.
  2. Wrong Content-Type value. Posting JSON with Content-Type: text/plain is the textbook 415.
  3. Wrong charset parameter. Some strict servers reject application/json; charset=utf-8 if they only accept application/json with no parameters.
  4. File upload format not in the allowlist. Endpoints that accept image/png and image/jpeg will 415 a request with image/webp.
  5. Missing or wrong Accept header. Less common, but some servers (especially RESTful APIs) check Accept and 415 if you ask for a response format they don't produce.
  6. Proxy or WAF stripping the Content-Type. A misconfigured Cloudflare WAF or corporate proxy can drop or rewrite headers, leaving the origin to see a different Content-Type than you sent.

How to Fix 415 in Each Library

curl

The default for curl --data is application/x-www-form-urlencoded. For JSON APIs, set the header explicitly:

curl -X POST https://api.example.com/v1/items   -H 'Content-Type: application/json'   -H 'Accept: application/json'   --data-raw '{"name":"sample","price":42}'

Python requests

Use the json= parameter (which sets Content-Type for you) instead of data= with a string:

import requests
# Correct — requests sets Content-Type: application/json automatically
r = requests.post("https://api.example.com/v1/items", json={"name": "sample", "price": 42})

# Also correct — explicit
r = requests.post(
    "https://api.example.com/v1/items",
    data='{"name":"sample","price":42}',
    headers={"Content-Type": "application/json", "Accept": "application/json"},
)

axios

// axios sets application/json automatically when you pass an object
await axios.post("https://api.example.com/v1/items", { name: "sample", price: 42 });

// Override explicitly if needed
await axios.post(url, body, { headers: { "Content-Type": "application/json" } });

fetch

// fetch does NOT set Content-Type automatically — you must set it
await fetch("https://api.example.com/v1/items", {
  method: "POST",
  headers: { "Content-Type": "application/json", "Accept": "application/json" },
  body: JSON.stringify({ name: "sample", price: 42 }),
});

415 vs 400 vs 422

Status Meaning Stage of processing
400 Bad RequestRequest is malformed — bad JSON syntax, missing required fieldBody parsing failed
415 Unsupported Media TypeContent-Type header doesn't match endpointBefore body parsing
422 Unprocessable EntityBody parses fine, but data fails validationAfter parsing, during business logic

Order of operations: server receives request → checks Content-Type (415 if mismatch) → parses body (400 if syntax error) → validates business rules (422 if invalid).

415 in Web Scraping With Proxies

When scraping API endpoints behind a residential or datacenter proxy, two extra causes of 415 to watch for:

  1. Proxy strips or normalizes Content-Type. Some shared HTTP proxies normalize all upstream Content-Type values to text/plain. Use a premium residential proxy or static datacenter proxy that passes headers through unmodified, and verify with SpyderProxy's HTTP headers tool.
  2. WAF rewrites the request before reaching origin. If the target site is behind Cloudflare, AWS CloudFront, or Akamai, the WAF can strip headers it considers suspicious. Inspect the request with SpyderProxy's proxy checker to see what reaches the origin.

If you're seeing 415 specifically when scraping behind a proxy but not when running the same request directly, the proxy is rewriting headers. Switch to a higher-trust proxy tier or use HTTPS end-to-end so the WAF can't read or modify the body.