Does o1-mini Support Function Calling?
8 min read · updated August 11, 2026
You sent a working tools array to o1-mini and got a 400 back. The request is not malformed and your schema is fine — the model does not accept the parameter at all, and it is one of several it does not accept.
The error you are looking at
The rejection comes back as an HTTP 400 with an invalid_request_error naming the offending parameter. Through the Python SDK it surfaces like this:
openai.BadRequestError: Error code: 400 - {
"error": {
"message": "Unsupported parameter: 'tools' is not supported with this model.",
"type": "invalid_request_error",
"param": "tools",
"code": "unsupported_parameter"
}
}The two fields to read are param and code. unsupported_parameter is a distinct code from invalid_value and from a schema validation failure, and the distinction is the diagnosis: this is not “your tools array is wrong”, it is “this model has no tools array”. No amount of editing the schema will change the response. The same code with a different param is what you get for the other unsupported parameters listed further down this page.
It is also worth checking which model string you actually sent. The o-series names are close enough that a configuration file carrying o1-mini where somebody meant o3-mini, or a stale environment variable that survived a migration, produces exactly this error while everyone reads the code that sets tools and finds nothing wrong with it. The model field of a successful response echoes what was served; on a 400 there is no response to read, so log the model string on the request side before concluding the model is at fault.
A different error is worth ruling out before you go further. A 404 with model_not_found means the model is not available to your account or organisation rather than that the parameter is unsupported — that is an access problem, not a capability one, and it has a different fix.
The short answer
No. o1-mini does not support function calling. OpenAI’s model documentation carries a capability table per model, and for the o1-mini entry the function-calling row is marked as unsupported, along with structured outputs and system messages. It is not a rate-limit thing, not a tier thing and not a preview-access thing; the endpoint rejects the parameter for every caller.
o1 (the 2024-12-17 snapshot and later) supports tools where the earlier previews did not, and the later o3 and o4-mini models support them as well. Check the capability table on OpenAI’s models page for the exact model string you are sending before concluding anything about a model other than o1-mini itself.Why the parameter is rejected rather than ignored
It would be technically easy for the API to drop an unsupported parameter and answer anyway. Rejecting is the better behaviour and the reason is worth internalising, because it generalises to the whole o-series capability surface.
A tool-calling request is not a request with an optional extra. The caller has written code that expects to branch on finish_reason: "tool_calls" and read message.tool_calls. If the parameter were silently ignored, the model would answer the user’s question in prose — plausibly, confidently, and without ever calling the function that was supposed to fetch the real data. The application would then present a fabricated answer where it intended to present a looked-up one. A 400 at the boundary is a bug you find in development; a silently dropped tools array is a bug you find in a customer complaint.
As for why the capability is missing in the first place: o1-mini shipped as a reasoning model in the first o-series release, and the tool-calling surface — along with structured outputs and the developer message role — arrived with later o-series models rather than being retrofitted to it. The reasoning-token mechanism itself does not preclude tool use; the later models demonstrate that. It simply was not in this one.
Four ways round it
- Route the tool-calling turns to a model that supports them. This is almost always right. A conversation is not obliged to use one model: send the turns that need a tool call to a model with tool support, and keep o1-mini for the turns that are pure reasoning over context you already assembled. The messages array is portable between them.
- Split reasoning from calling into two requests. Ask o1-mini to reason and to state, in plain text, what it needs and with what arguments. Then pass that text to a cheap model with
toolsattached and let it emit the actual structured call. This costs a second round trip and buys you o1-mini’s reasoning on the decision without needing it to format the call. - Ask for JSON in the prompt and parse it yourself. Feasible, but note that o1-mini does not support response_format with a json_schema either, so there is no server-side guarantee of validity. You are writing the parser, the validator and the retry loop by hand, and you must handle the case where the model wraps its JSON in prose. Do this only if the tool call is genuinely low-stakes.
- Do the retrieval before the call. If the reason you wanted a tool is that the model needs data, fetch the data with ordinary code and put it in the prompt. A reasoning model with the facts already in context does not need to ask for them, and this removes a round trip rather than adding one. It only fails when the model has to decide which data to fetch.
What does not work: retrying, changing the schema, using the legacy functions parameter instead of tools (also unsupported, same error with a different param), or setting tool_choice. All four produce the same 400.
The other limits in the same family
If tools were rejected, the next parameter you reach for probably will be too. The unsupported set for o1-mini clusters, and knowing the whole set saves you finding them one 400 at a time:
- System messages. A
role: "system"message is rejected — the error namesmessages[0].rolerather than a top-level parameter. Fold the instructions into the first user message instead. Thedeveloperrole that replacedsystemon later o-series models is not available here either. - Structured outputs. No
json_schemaresponse format. - Sampling parameters.
temperature,top_pand the penalties are fixed rather than caller-set on the o-series. - Image input. Text only.
- max_tokens. Use
max_completion_tokens, and remember it includes the reasoning tokens you never see, so a small budget can be spent entirely on reasoning and return an empty answer.
If you support more than one model behind a single feature, the durable shape is a capability table in your own code rather than a try-and-catch around each parameter. Keep a small record per model string saying whether it accepts tools, a system role, a schema response format and caller-set sampling, and build the request from that record. It is a few lines, it fails at the point where somebody added a model rather than at three in the morning, and it makes the fallback explicit: this model cannot call tools, so this request goes to the one that can. Catching a 400 and retrying without the parameter works, but it doubles the latency of every affected request and it hides the capability gap from everyone reading the code afterwards.
The general lesson is that on the o-series the model string, not the endpoint, decides which parameters exist. Code written against gpt-4o and pointed at an o-series model by changing one string will hit this class of error repeatedly, and the fastest way through is to read the capability row for the exact snapshot once rather than to discover it a parameter at a time.