I haven't found the docs for that use case. How can I get the request body, ensure it's a valid JSON (any valid JSON, including numbers, string, booleans, and nulls, not only objects and arrays) and get the actual JSON. Using Pydantic forces the JSON to have a specific structure.
8 Answers
You can find nearly everything inside the Request
object
You are able to get request body with request.json()
, which will give you the parsed JSON as dictionary.
from fastapi import Request, FastAPI
@app.post("/dummypath")
async def get_body(request: Request):
return await request.json()
If you want access the body as string, you can use request.body()

- 16,595
- 7
- 65
- 85
-
1In the `edit` history of this answer it seems that `request.body()` has been replaced by `request.json()` only inside the code block :-) – tgogos May 17 '21 at 10:37
-
-
5I make a request with form-data selected in the body and get this error "ValueError: [TypeError("'coroutine' object is not iterable"), TypeError('vars() argument must have __dict__ attribute')]" – Parashuram May 24 '21 at 17:56
-
-
I am trying to access the body for error logging. when I try to access the body with request.body() from a logger, I get "
" instead of the body as a string. – Naveen Reddy Marthala Jan 26 '22 at 16:55 -
Use `await` if you are getting coroutine object. That means that file descriptor is still waiting. – Yagiz Degirmenci Jan 26 '22 at 19:37
-
Why the public method `json` from the class or module `Request` is a `coroutine`?, I should read the FastAPI docs. – Franco Gil Apr 15 '22 at 01:46
-
I am using this code, without the async clause, but I still get a coroutine. How is it possible? – Christian Sicari Nov 17 '22 at 16:58
-
It is so simple. If you are getting coroutine you are not using `await`. – Yagiz Degirmenci Nov 17 '22 at 22:56
-
1@ChristianSicari Please have a look at [this answer](https://stackoverflow.com/a/70659178/17865804), if you would like to get the raw `body` or JSON (using `.json()`, which actually returns a `dict` object, as shown [here](https://github.com/encode/starlette/blob/88e9fc1411f6bd79131afa4a7d2f4dc576c8bf04/starlette/requests.py#L239)) in an endpoint defined with normal `def` instead of `async def`. – Chris Nov 27 '22 at 06:47
-
@parashuram the other highly voted answer which uses Body(...) fixes this issue for me – Matt Jul 03 '23 at 22:29
The accepted answer is valid as well, but FastAPI provides a built-in way to do that - check the Singular values in body section in docs.
A parameter with the default Body
gets all the payload that doesn't match passed Pydantic-typed parameters (the whole payload in our case) and converts it to the dict. In case of invalid JSON, a standard validation error would be produced.
from fastapi import Body, FastAPI
app = FastAPI()
@app.post('/test')
async def update_item(
payload: dict = Body(...)
):
return payload
UPD: Note on ...
(Ellipsis) - it allows marking a value as required. Read more in the Required with Ellipsis docs section

- 6,998
- 3
- 22
- 36
-
1`dict = Body()` for the win. This simple line has eluded me for far too long. – zelusp Feb 10 '23 at 18:44
-
1@zelusp I don't know why `request.json()` is recommended and used so often alongside with the regular arg typing stuff. To my mind `Body()` is the most natural way for FastAPI unless you need to deal with a complex body processing or so – Oleh Rybalchenko Feb 14 '23 at 16:11
-
3This is undeniably the correct way to do this and should be the accepted answer. – brandonscript Mar 01 '23 at 03:34
If you are confident that the incoming data is "a valid JSON", you can create a simple type annotation structure to receive the arbitrary JSON data.
from fastapi import FastAPI
from typing import Any, Dict, AnyStr, List, Union
app = FastAPI()
JSONObject = Dict[AnyStr, Any]
JSONArray = List[Any]
JSONStructure = Union[JSONArray, JSONObject]
@app.post("/")
async def root(arbitrary_json: JSONStructure = None):
return {"received_data": arbitrary_json}
Examples
1. JSON object
curl -X POST "http://0.0.0.0:6022/" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"test_key\":\"test_val\"}"
Response:
{
"received_data": {
"test_key": "test_val"
}
}
2. JSON array
curl -X POST "http://0.0.0.0:6022/" -H "accept: application/json" -H "Content-Type: application/json" -d "[\"foo\",\"bar\"]"
Response:
{
"received_data": [
"foo",
"bar"
]
}
If you are not sure about the content type of the incoming data, better to parse the request body.
It can be done as,
from fastapi import FastAPI, Request
app = FastAPI()
@app.post("/")
async def root(request: Request):
return {"received_request_body": await request.body()}
The advantage of this method is that the body will contain any kind of data, JSON, form-data, multipart-form-data, etc.

- 82,442
- 19
- 127
- 206
-
2That is not right, if it is not a valid JSON, Pydantic would throw an error. In addition to that there is nothing called "JSON array" Python parses JSON as Dictionary, so OP 's question is very clear, he wants to get actual JSON which is unparsed one and there is only way to get actual JSON is from `request.body()`. – Yagiz Degirmenci Oct 16 '20 at 10:26
-
@YagizcanDegirmenci Yes, there is something called [**JSON Array**](https://www.w3schools.com/js/js_json_arrays.asp) which cant not be parsed into a Python ***dict***, but a Python ***list*** – JPG Oct 16 '20 at 11:05
-
Yes, I'm pretty sure OP did not mean JSON Array in the question. – Yagiz Degirmenci Oct 16 '20 at 11:15
-
OP didn't mention JSON Object too. This answer is meant for those who want to get an *arbitrary JSON( either array or object)* from the request ***not as a string***, as I already mentioned – JPG Oct 16 '20 at 11:21
-
3Actually, I want ANYTHING that is a valid JSON. Including numbers, booleans, null and strings. Not only objects and arrays. – caeus Oct 16 '20 at 20:23
from fastapi import Request
async def synonyms__select(request: Request):
return await request.json()
will return a JSON object.

- 4,812
- 4
- 30
- 53
This is an example to print the content of a Request
, it will print the json body (if it is json parsable) otherwise just print the raw bytes of the body.
async def print_request(request):
print(f'request header : {dict(request.headers.items())}' )
print(f'request query params : {dict(request.query_params.items())}')
try :
print(f'request json : {await request.json()}')
except Exception as err:
# could not parse json
print(f'request body : {await request.body()}')
@app.post("/printREQUEST")
async def create_file(request: Request):
try:
await print_request(request)
return {"status": "OK"}
except Exception as err:
logging.error(f'could not print REQUEST: {err}')
return {"status": "ERR"}

- 506
- 5
- 7
FastAPI has a JSON encoder.
There are some cases where you might need to convert a data type (like a Pydantic model) to something compatible with JSON
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
import simplejson as json
class SubmitGeneral(BaseModel):
controllerIPaddress: str
readerIPaddress: str
ntpServer: str
@app.post("/submitGeneral")
async def submitGeneral(data: SubmitGeneral):
data = jsonable_encoder(data)
#data = json.loads(data.json()) # same as above line
print(f"data = {json.dumps(data)}")
# you have to access the properties with brackets, not by dot notation
query = f"update LocalPLC set ControllerIpAddress = '{data['controllerIPaddress']}', ReaderIPAddress = '{data['readerIPaddress']}'"
return {"status": "OK"}

- 3,213
- 3
- 40
- 59
For those of you using BaseModel and want to have a JSON field, you can import Json from pydantic
from fastapi import FastAPI
from pydantic import BaseModel, Json, Field
app = FastAPI()
class MockEndpoint(BaseModel):
endpoint: str = Field(description="API endpoint to mock")
response: Json = Field(description="Example response of the endpoint")
@app.get("/")
async def root():
return {"message": "Hello World"}
@app.post("/mock")
async def mock_request(mock_endpoint: MockEndpoint):
return mock_endpoint

- 1
This is also another method to get any form of Json as your input
@app.post("/dict/")
async def post_dict(data: Dict[str, Any]):
return data
But I guess its not the cleanest way of doing things

- 51
- 1
- 2