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.
Loved by over 4 million developers and more than 90,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.