POST request to FastAPI endpoint produces error 422: "value is not a valid dict"
David Y.
—I have a Python script that sends a POST request to an API I’ve built using FastAPI. The POST request fails with error code 422 and the following details:
{ "detail": [ { "loc": [ "body" ], "msg": "value is not a valid dict", "type": "type_error.dict" } ] }
My client script looks like this:
import requests, pandas, json dataframe = pandas.DataFrame({ "Firstname": ["Joaquin"], "Surname": ["Phoenix"], "Age": [49] }) df_json = dataframe.to_json(orient="records") response = requests.post('http://localhost:8000/create-user/', json=json.loads(df_json), headers={"Content-Type": "application/json"}) print(response.json())
The relevant API endpoint and Pydantic model look like this:
from pydantic import BaseModel from fastapi import FastAPI app = FastAPI() class User(BaseModel): firstname: str surname: str age: int @app.post("/create-user/") async def create_users(user: User): # ... user creation operations ... return {"Message": "User created."}
What’s going wrong? Is it a problem with my client script or my API code?
The problem is in the client script. The requests.post
function expects a dictionary to be passed to the json
keyword argument, which it will automatically convert to JSON.
This script first converts a Pandas DataFrame to a JSON string (dataframe.to_json(orient="records")
), which looks like this:
[{"Firstname":"Joaquin", "Surname": "Phoenix","Age": 49}]
The script then uses json.loads
to convert it back into a Python object. Because of the argument orient="records"
in the original conversion, the object created here will be a list rather than a dictionary, leading to the 422 error when it is submitted.
We can fix this problem by converting the DataFrame to a dictionary instead of a JSON string:
import requests, pandas dataframe = pandas.DataFrame({ "Firstname": ["Joaquin"], "Surname": ["Phoenix"], "Age": [49] }) userdata = dataframe.to_dict(orient="records") # will be a list: [{"Firstname":"Joaquin", "Surname": "Phoenix","Age": 49}] userdata = userdata[0] # take the first item from the list # will be a dictionary: {"Firstname":"Joaquin", "Surname": "Phoenix","Age": 49} prediction = requests.post('http://localhost:8000/create-users/', json=userdata, headers={"Content-Type": "application/json"})
This altered code should produce the expected results.
Tasty treats for web developers brought to you by Sentry. Get tips and tricks from Wes Bos and Scott Tolinski.
SEE EPISODESConsidered “not bad” by 4 million developers and more than 100,000 organizations worldwide, Sentry provides code-level observability to many of the world’s best-known companies like Disney, Peloton, Cloudflare, Eventbrite, Slack, Supercell, and Rockstar Games. Each month we process billions of exceptions from the most popular products on the internet.