Groq API 400 Bad Request
Groq speaks the OpenAI wire protocol, which is why the most common Groq 400 is a parameter that is perfectly valid — on OpenAI.
📡 Monitor your APIs — know when they go down before your users do
Better Stack checks uptime every 30 seconds with instant Slack, email & SMS alerts. Free tier available.
Affiliate link — we may earn a commission at no extra cost to you
Live Groq status right now
Check this first only to close it off. A 400 is never an outage — a service that is down cannot read your payload well enough to object to it — but ruling the platform out in thirty seconds is cheaper than arguing about it for an hour.
A 400 is the only refusal that is genuinely about what you sent. The connection succeeded, TLS completed, the credential was accepted, your quota was never in question — and then Groq read the body and could not use it. No tokens were generated. Nothing was billed. Nothing half-happened, which is the one mercy this error offers compared with a 500.
That makes it the most tractable error in the catalogue and the one most often debugged in the wrong place. Teams open the status page, check the billing dashboard and re-read the retry policy, when the entire fault surface is a few hundred bytes of JSON that are sitting in a variable one stack frame away. The response body names the offending field. Almost nobody reads it, because the SDK threw an exception before anyone got the chance.
30-second triage: serialise the exact body your client was about to send, write it to a file, and replay it with curl. The raw response will name the field Groq objected to. If curl reproduces the 400, the bug is in how you built the request and no amount of retrying, failing over or checking live Groq status will change it. If curl succeeds with the same bytes, your client is not sending what you think it is sending — look at middleware, a proxy, or a serialiser that drops or adds fields.
400, 401, 404, 422 and 429 are five different problems
These get collapsed into “the API rejected us” and handled by one branch, which is how a malformed body ends up in a retry loop and a validation bug ends up being escalated as an incident. The distinction is how far the request got before it stopped.
| 400 Bad Request | 401 Unauthorized | 404 Not Found | 422 Unprocessable | 429 Too Many Requests | |
|---|---|---|---|---|---|
| What was refused | The shape of what you sent | Who you are | The thing you named | The values you sent | How much you asked for |
| Was the body parsed? | Attempted, then rejected | Never looked at | Routed before parsing | Parsed successfully | Usually not reached |
| Does retrying help? | Never — deterministic | Never | Never | Never | Yes, after a backoff |
| Who fixes it | Whoever builds the request | Secret-store owner | Whoever pins model ids | Whoever owns the input data | Whoever controls pacing |
| Counts as Groq downtime? | Never | Never | Never | Never | Never |
The row that costs the most time is the third. A client that applies one retry policy to every non-2xx response will hammer a deterministic refusal until its deadline expires, converting an error you could have shown in milliseconds into a request that hangs for the full retry budget and then times out — and a timeout is the one symptom that genuinely does look like an outage. That is how a malformed body gets escalated to an on-call engineer. The full map is in the Groq API error codes reference.
Separate Your Bad Requests From Their Bad Days
External checks run from outside your infrastructure with their own known-good payload, so a 400 in your application is never debugged as a possible provider incident — you can see the provider answering a valid request perfectly well at the same moment.
Try Better Stack Free →Read the response body, not the SDK exception
A 400 is the error where the provider actually tells you the answer, and the SDK is the thing most likely to throw it away. Most clients collapse a validation failure into a generic exception with a message that has been through two layers of wrapping: the status code survives, and the JSON body naming the offending field frequently does not. Replay the call at the wire level before forming a theory:
# Write the exact body your client builds to a file, then replay it verbatim: curl -i -s -X POST https://api.groq.com/openai/v1/chat/completions \ -H "Authorization: Bearer $GROQ_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @body.json # Prove the bytes are valid JSON before blaming the schema: python3 -m json.tool < body.json > /dev/null && echo "parses"
Those two commands split the category in half. If the file does not parse, you have a serialisation bug and the schema is irrelevant. If it parses and Groq still objects, the response body names the field, and you are done. The structural fix is to log the outgoing body — redacted, and only on a 400 — so the next occurrence is diagnosed from a log line instead of a reproduction. See the Groq API testing guide for wiring that into a contract test rather than discovering it in production.
The Groq trap: an OpenAI-compatible surface is not an OpenAI-identical one
Groq's chat endpoint is deliberately OpenAI-compatible, so the SDK you already have works without modification — right up until you send a parameter OpenAI supports and Groq does not. logprobs, top_logprobs, logit_bias and an n greater than 1 are the documented ones. The request is otherwise flawless: valid key, valid model, well-formed messages, and a 400 anyway. Nothing in the error is about your prompt, and that is exactly why teams spend an hour on the prompt.
The failure is release-shaped rather than random. The offending parameter almost never arrives on its own — it arrives inside a shared request-builder that was written against OpenAI and then reused for the Groq path because the two speak the same protocol. That means the 400 appears at a deploy, affects one hundred per cent of calls through that helper, and affects none of the calls that construct their bodies elsewhere. A clean split like that in your error rate is the diagnosis before you have read a single response body.
The second Groq-specific shape is prompt-dependent and therefore much nastier. Requesting response_format: {"type": "json_object"} requires the word JSON to appear somewhere in the messages; a prompt reworded to say “return the fields as structured data” gets a 400 while every other input through the same code path succeeds. Because the base URL is an override rather than a distinct client type, nothing in your code marks which provider a given call is bound for. Construct a separate client per provider and keep the parameter builders separate, so the compatible-but-not-identical surface is a visible boundary rather than an ambient assumption.
The six causes, in the order worth checking
Work down this list rather than across it. Each check is cheap, and the ordering reflects which answers turn out to be correct most often rather than which are most interesting.
- An OpenAI-only parameter reached the Groq endpoint. A shared request-builder added
logprobs,logit_biasorn > 1. Valid everywhere else; rejected here. - The JSON never parsed. A trailing comma, a
NaN, or an unescaped control character that arrived inside user-supplied text. The body looks fine in a log because the log printed the object, not the bytes. - A field has the right name and the wrong type.
max_tokensas a string,temperatureas a string, a boolean as"true". Anything that has been through a form, a query string or an environment variable is a suspect. - A content bound was exceeded.
max_tokensabove the model’s window, too many messages, an attachment over the size cap. These are input-dependent, so they fire for one request in a thousand and look intermittent. - A tool or function schema is not valid JSON Schema. Hand-written parameter definitions with a missing
typeor a malformedrequiredarray are rejected as part of the request, not at call time. - A null was sent where the field should have been omitted. Serialisers that emit every key make optional parameters explicit nulls, and an explicit null is a value the validator has to reject.
Notice the split running through that list. The first two are deploy-shaped: they appear at a release, affect a clean fraction of traffic, and are identical every time. The last three are input-shaped: they fire on one request in a thousand, look intermittent, and are usually triggered by something a user typed. Which of those two patterns your error rate shows is worth more than any individual hypothesis, and it is visible before you have read a single response body. If tool schemas are involved, the Groq tool calling guide and the structured output guide cover the shapes that are accepted.
Make the next one impossible instead of fast
A 400 is a ten-minute fix that recurs forever unless the conditions that produced it change. Four structural changes remove most of the category permanently:
- Validate the body against a schema before it leaves your process. A local validator rejects the same request in microseconds, with a stack trace pointing at the line that built it, instead of a round trip and a field name. This is the single highest-leverage change and almost nobody makes it.
- Build requests in one place, per provider. Most deploy-shaped 400s come from a request-builder shared across providers whose parameter sets have quietly diverged. One builder per provider makes the divergence a compile-time fact rather than a runtime discovery.
- Log the outgoing body on a 400 only, redacted. The information needed to fix this error exists at the moment it happens and is discarded microseconds later. Capturing it turns every future occurrence from a reproduction exercise into a log lookup.
- Alert on 4xx separately from 5xx. A 400 rolled into a general error-rate alert either pages the wrong person or hides in noise. It has a different owner, a different runbook and a different urgency — and unlike a 5xx, it will not resolve itself. The setup is in the Groq status alerts guide.
- Pin the SDK, and read the changelog on majors. Parameter sets and request shapes change between major versions, and a 400 arriving the day after a dependency bump is not a coincidence. The Groq SDK version guide covers what moves.
Frequently Asked Questions
What does a 400 from the Groq API actually mean?
It means the request reached Groq, was parsed, and was found unusable on its contents. That is a narrower statement than it sounds, and the narrowness is the useful part: a 400 is decided after the credential was accepted and before any model work began, so it says nothing about your key, nothing about your quota, and nothing about capacity. Everything you would normally check when a call fails — the status page, the billing dashboard, your rate limiter — is on the wrong side of the point at which this one stopped. The entire fault surface is the bytes you sent.
Is a Groq 400 ever caused by Groq being down?
No, and the reasoning is worth keeping because it inverts the usual instinct. A 400 is a considered answer: something on the far end read your payload, understood it well enough to judge it, and refused. A service that is down cannot do that. So a 400 should never be counted against Groq availability in your own metrics, and a monitor that treats every non-2xx response as downtime will page you for your own malformed request. The one edge case is a proxy, gateway or WAF in front of the API returning its own 400 — a header too large, a URL too long — which you can distinguish immediately because the body will not be Groq's JSON error shape.
Should I retry a 400?
Not with the same body, ever. A 400 is deterministic: the identical request will be refused identically every time, so a backoff loop converts one instant refusal into dozens of them plus the full retry budget in latency, and delivers a timeout to your user instead of an error you could have shown them in milliseconds. If your client applies one policy to all non-2xx responses, this is the error that proves 4xx and 5xx need splitting. Fail fast, log the response body — which names the offending field — and surface it to an engineer rather than to a customer. A 400 is only retryable after the body has actually changed.
How do I tell a Groq 400 from a 422?
By asking how far the request got before it was rejected. A 400 means the request could not be understood as a valid request at all: unparseable JSON, an unknown parameter, a field of the wrong type, a model id that is not a model id. A 422 means the request was understood perfectly and the values in it are not acceptable — well-formed, correctly typed, semantically wrong. The distinction matters because it tells you where to look. A 400 is usually a code or configuration bug you find by diffing your body against the schema; a 422 is usually a data bug you find by looking at the specific values that arrived. Providers are not always rigorous about which they return, so treat the response body as authoritative over the code.
Why does the same code work against OpenAI and 400 against Groq?
Because compatible is not identical. Groq implements the OpenAI wire protocol, so the transport, the auth scheme and the message format all match — which is what makes the failure surprising. What does not match is the accepted parameter set: logprobs, top_logprobs, logit_bias and an n above 1 are rejected outright rather than ignored. Rejecting rather than ignoring is the right behaviour, because silently dropping a parameter you asked for would produce a wrong answer instead of an error, but it does mean a request-builder shared between the two providers will pass review, pass staging against OpenAI, and fail one hundred per cent of Groq traffic the moment it ships.
My Groq JSON mode request 400s for some prompts and not others. Why?
Because JSON mode has a prompt-level precondition, not just a parameter-level one. When response_format is set to json_object, the word JSON must appear somewhere in the messages, and a prompt that asks for 'structured data' or 'the fields as an object' does not satisfy it. This is the only Groq 400 whose trigger lives in content rather than in code, which is why it survives testing: the code path is exercised constantly and only the reworded template fails. If your prompts are stored in a database or edited by non-engineers, add the check to whatever validates them on save, because by the time it reaches the API it is an outage for one tenant and invisible to everyone else.
Related Groq Guides
Stop Escalating Your Own Payload as a Groq Outage
API Status Check watches Groq and the rest of your stack from outside your infrastructure with its own known-good request — so a malformed body never gets escalated as an incident, and a real incident never gets dismissed as somebody’s bad JSON.
Start Your Free Trial →Alert Pro
14-day free trialStop checking — get alerted instantly
Alert Pro checks the 60+ APIs we monitor every hour and emails you within the hour of a detected change.
- Email alerts for up to 10 of the APIs we monitor
- $0 charged today — card required to start
- Cancel anytime — $9/mo after trial
🌐 Can't Access Groq?
If Groq is working for others but not for you, it might be an ISP or regional issue. A VPN can help bypass network-level blocks and routing problems.
Troubleshoot with a VPN
Connect from a different region to test if the issue is local to your network. Also protects your connection on public Wi-Fi.
Try NordVPN — 30-Day Money-Back GuaranteeSecure Your Groq Account
Service outages are a common time for phishing attacks. Use a password manager to keep unique, strong passwords for every account.
Try NordPass — Free Password Manager⏳ While You Wait — Try These Alternatives
🛠 Tools We Use & Recommend
Tested across our own infrastructure monitoring 200+ APIs daily
SEO & Site Performance Monitoring
Used by 10M+ marketers
Track your site health, uptime, search rankings, and competitor movements from one dashboard.
“We use SEMrush to track how our API status pages rank and catch site health issues early.”