SendGrid 406: Not Acceptable

SendGrid 406 Not Acceptable fires when a v3 API request lacks a valid Accept header. Add Accept: application/json — SDKs set this; check raw HTTP clients.

Updated Jul 1, 2026

The short answer

SendGrid returns HTTP 406 Not Acceptable when your v3 API request omits an Accept header the server can satisfy (or sends one it cannot, like application/xml). SendGrid's v3 reference says to send Accept: application/json, which official SDKs already do; SendGrid Support's 4XX article instead recommends removing the Accept header. The two sources disagree, so add the header first, then remove it if that fails. Intermittent 406s without code changes usually indicate a SendGrid incident.

SendGrid's v3 API returns HTTP 406 Not Acceptable during content negotiation: the server cannot return a response in a format your request says it will accept. In practice this almost always means the Accept request header is missing (or set to a media type SendGrid can't produce). 406 is a standard HTTP status (RFC 9110 §15.5.7), but the specific trigger here is SendGrid-side.

What causes SendGrid 406: Not Acceptable?

  • Missing Accept header (most common). SendGrid's official v3 API reference lists 406 directly as "Missing the Accept header." SendGrid's v3 API is JSON-only in practice, so the client is expected to advertise that it accepts JSON.
  • An Accept header SendGrid can't satisfy. Sending something like Accept: application/xml or Accept: text/html asks for a representation the JSON-only API cannot produce, which also yields 406.
  • A raw / hand-rolled HTTP client. Official SDKs including sendgrid-python set Accept: application/json in their default headers; other official SendGrid SDKs (PHP, Node.js, Ruby, C#, Go, Java) are expected to do the same based on historical fixes (e.g., sendgrid-php #177 added the header to that client), though this hasn't been independently re-verified for every listed language's current version. 406 typically appears when you call api.sendgrid.com directly via cURL, fetch, requests, Guzzle, an integration platform, or an old SDK build that predates the fix.
  • Intermittent 406s with no code change. If requests succeed and fail with otherwise-identical code, the cause is usually a SendGrid platform incident, not your headers. The Auth0 community thread traced a wave of intermittent 406s to a SendGrid-side incident (a documented "406 Errors for API Requests" incident), and the sporadic, non-deterministic 406s reported in sendgrid-python #149 fit the same pattern. Check status.sendgrid.com before changing code.

A note on conflicting official guidance: SendGrid's own docs disagree with each other on 406. The v3 API reference says it's caused by a missing Accept header and that the fix is to add Accept: application/json. SendGrid Support's 4XX troubleshooting article, by contrast, describes the cause as an inability to negotiate the Accept header and instructs readers to remove the Accept header entirely and resubmit. To make it more confusing, that same Support article's own example of the "problematic" header is itself Accept: application/json — the exact value the v3 reference recommends adding — which reads as either a documentation error on SendGrid's part or a reference to some other malformed/duplicate header state that isn't clearly explained. In practice: add Accept: application/json first, since that matches current SDK behavior and the v3 reference; if the 406 persists, try removing the Accept header entirely per SendGrid Support's guidance, and consider contacting SendGrid support directly since their own docs disagree.

How do I fix SendGrid 406: Not Acceptable?

Add an Accept: application/json header to the request (and make sure Content-Type: application/json is set on requests with a body). Using the current v3 Mail Send endpoint:

curl --request POST \
--url https://api.sendgrid.com/v3/mail/send \
--header 'Authorization: Bearer YOUR_SENDGRID_API_KEY' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '{
"personalizations": [{"to": [{"email": "recipient@example.com"}]}],
"from": {"email": "verified_sender@example.com"},
"subject": "Test Subject",
"content": [{"type": "text/plain", "value": "testing text body"}]
}'

In code, prefer the official SDK, which sets these headers for you:

import os
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail
message = Mail(
from_email="verified_sender@example.com",
to_emails="recipient@example.com",
subject="Test Subject",
plain_text_content="testing text body",
)
sg = SendGridAPIClient(os.environ["SENDGRID_API_KEY"])
response = sg.send(message) # SDK sends Accept: application/json

Checklist:

  1. Set Accept: application/json on every request; don't send Accept: application/xml or other non-JSON types. If that doesn't clear the 406, removing the Accept header entirely (so a JSON default applies) is SendGrid Support's documented alternate fix — see the conflicting-guidance note above.
  2. If you're on an old SDK version, upgrade — the header was added to the official clients years ago.
  3. If 406s are intermittent and your headers are correct, check SendGrid Status and retry with backoff rather than altering request code.

Note: the legacy v2 Web API (/api/mail.send.json with api_user/api_key form fields) is deprecated — SendGrid strongly recommends migrating to the v3 Bearer API-key endpoints shown above. (The v2 endpoint still functions, but it no longer receives new features and should not be used for new integrations.)

FAQ

Common questions

SendGrid's v3 API reference lists 406 as 'missing the Accept header,' and the practical fix is to always send Accept: application/json. The official SDKs set it automatically, so you only need to add it manually when calling the API with a raw HTTP client. If adding the header doesn't resolve it, SendGrid Support's troubleshooting doc for 406 also recommends removing the Accept header entirely as an alternate fix — see below.

One API, every provider

Stop debugging raw provider errors

Courier connects to your email, SMS, and push providers, handles retries and failover, and surfaces delivery errors in plain language.

Reply-code definitions per RFC 9110 §15.5.7. Last reviewed Jul 1, 2026. Courier is not affiliated with third-party providers; error behavior may vary by implementation.