Request & Response Objects
Reviewed & published by Brayan K
The request object is Flask's window into the incoming HTTP request — query parameters, form data, headers, and JSON — while the response is whatever your view returns to the browser.
Part of the free Flask course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
In this lesson you'll read data the client sends, branch on the HTTP method, and craft responses with custom status codes, headers, and JSON.
What You'll Learn in This Lesson
- What the global request object contains
- Reading query strings with request.args
- Reading form data with request.form
- Checking request.method to handle GET vs POST
- Returning custom status codes and headers
- Sending JSON responses with jsonify
1 Reading the Request
The request object is a special global that Flask populates for each incoming request. You import it once and read from it inside any view function.
Two of its most useful attributes are request.args (query-string values from the URL) and request.form (data submitted in a POST body). Both behave like dictionaries, and .get() lets you supply a default when a key is missing.
The example below reads a name query parameter — for a URL like /greet?name=Ada it returns a personalized greeting. If no name is supplied, it falls back to "stranger".
# app.py — read query-string parameters from the request
from flask import Flask, request
app = Flask(__name__)
@app.route("/greet")
def greet():
# request.args holds query-string values: /greet?name=Ada
# .get() returns a default when the key is missing
name = request.args.get("name", "stranger")
return f"Hello, {name}!"
# Simulate two requests without starting a server:
with app.test_client() as client:
print(client.get("/greet?name=Ada").data.decode()) # Hello, Ada!
print(client.get("/greet").data.decode()) # Hello, stranger!
# app.run() would serve this forever, so it goes last and stays commented
# out — the test client above is how Flask itself exercises routes:
# if __name__ == "__main__":
# app.run(debug=True)
# ✅ Expected output:
# Hello, Ada!
# Hello, stranger!Run this and the test client prints Hello, Ada! for the first request and Hello, stranger! for the second, proving the default kicks in when name is absent.
2 Handling GET and POST
A single route can respond to multiple HTTP methods. List them in the decorator with methods=["GET", "POST"], then check request.method inside the view to decide what to do.
The example below shows a form on a GET request and reads the submitted value from request.form on a POST. The POST branch returns a confirmation message built from the data the user typed.
# app.py — one route, two methods
from flask import Flask, request
app = Flask(__name__)
@app.route("/submit", methods=["GET", "POST"])
def submit():
if request.method == "POST":
# request.form holds data posted from an HTML form
username = request.form.get("username", "")
return f"Thanks for signing up, {username}!"
# GET: show a simple form
return """
<form method="POST">
<input name="username" placeholder="Your name">
<button type="submit">Sign up</button>
</form>
"""
# Simulate a GET (shows form) and a POST (reads form data):
with app.test_client() as client:
print(client.get("/submit").status_code) # 200
posted = client.post("/submit", data={"username": "Grace"})
print(posted.data.decode()) # Thanks for signing up, Grace!
# app.run() would serve this forever, so it goes last and stays commented
# out — the test client above is how Flask itself exercises routes:
# if __name__ == "__main__":
# app.run(debug=True)
# ✅ Expected output:
# 200
# Thanks for signing up, Grace!3 Building Custom Responses
By default a view returns a string with status 200 OK. To control the status code, return a tuple of (body, status). To set headers too, return (body, status, headers) or build a response with make_response(...).
For APIs, jsonify(...) serializes a dictionary to JSON and sets the right Content-Type. The example below returns JSON with an explicit 201 Created status and a custom header.
# app.py — custom status codes, headers, and JSON
from flask import Flask, jsonify, make_response
app = Flask(__name__)
@app.route("/api/item")
def create_item():
# jsonify turns a dict into a JSON response
response = make_response(jsonify(id=42, name="Widget"))
response.status_code = 201 # 201 Created
response.headers["X-Powered-By"] = "Flask"
return response
@app.route("/missing")
def missing():
# Tuple form: (body, status_code)
return "Not here", 404
# Inspect the responses:
with app.test_client() as client:
r = client.get("/api/item")
print(r.status_code, r.headers["X-Powered-By"], r.data.decode()) # 201 Flask {"id":42,...}
print(client.get("/missing").status_code) # 404
# app.run() would serve this forever, so it goes last and stays commented
# out — the test client above is how Flask itself exercises routes:
# if __name__ == "__main__":
# app.run(debug=True)
# ✅ Expected output:
# 201 Flask {"id":42,"name":"Widget"}
#
# 404🔬 Request & Response Cheat Sheet
| You want to... | Use |
|---|---|
| Read a query param | request.args.get("q") |
| Read posted form data | request.form.get("field") |
| Know the HTTP method | request.method |
| Read a JSON body | request.get_json() |
| Return JSON | jsonify(data) |
🎯 Your Turn: Fill in the Blanks
Complete the route below. Replace each ___ so it accepts GET and POST, reads the posted email, and returns JSON.
# 🎯 YOUR TURN: replace each ___ to finish the route
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/subscribe", methods=["GET", "___"]) # allow POST too
def subscribe():
if request.___ == "POST": # which attribute holds the method?
email = request.___.get("email", "") # read posted form data
return ___(status="subscribed", email=email) # return JSON
return "Send a POST with an email field"
with app.test_client() as client:
r = client.post("/subscribe", data={"email": "a@b.com"})
print(r.get_json())Common Errors & Quick Reference
❌ 405 Method Not Allowed on POST
Your route only allows GET. Add the method to the decorator: methods=["GET", "POST"]. By default a route accepts GET only.
❌ 400 Bad Request reading a missing key
Indexing a missing key raises an error and returns 400. Prefer request.form.get("name"), which returns None (or your default) instead of crashing.
| Task | Code |
|---|---|
| Allow GET + POST | methods=["GET", "POST"] |
| Safe form read | request.form.get("name", "") |
| Custom status | return body, 201 |
| Return JSON | return jsonify(ok=True) |
🏆 Mini Challenge: A Tiny Echo API
Build a route that echoes back a query parameter as JSON.
- Read a msg value from request.args
- Default it to "hello" when missing
- Return JSON containing the echoed message and its length
# 🏆 Mini Challenge: an echo API
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/echo")
def echo():
msg = request.args.get("msg", "hello")
return jsonify(message=msg, length=len(msg))
# Try it:
with app.test_client() as client:
print(client.get("/echo?msg=flask").get_json()) # {'length': 5, 'message': 'flask'}
print(client.get("/echo").get_json()) # {'length': 5, 'message': 'hello'}❓ Frequently Asked Questions
Lesson complete — you control both sides of the conversation!
You can now read query strings and form data from request, branch on request.method, and return responses with custom status codes, headers, and JSON.
Practice quiz
Where do query-string values (after the ?) appear?
- request.form
- request.json
- request.args
- request.cookies
Answer: request.args. request.args holds URL query-string parameters.
Where does data submitted in a POST form body appear?
- request.args
- request.path
- request.headers
- request.form
Answer: request.form. request.form holds data posted in an HTML form body.
Which attribute tells you the HTTP method of the request?
- request.method
- request.verb
- request.type
- request.action
Answer: request.method. request.method holds the method string like "GET" or "POST".
How do you return a body with a custom status code?
- return body.status(404)
- return (body, status) tuple
- set request.status
- return status(body)
Answer: return (body, status) tuple. Returning a (body, status) tuple sets the HTTP status code.
Which helper serializes a dict to a JSON response with the right Content-Type?
- str(data)
- json.dumps(data)
- jsonify(data)
- render_json(data)
Answer: jsonify(data). jsonify(...) serializes to JSON and sets application/json.
Which function builds a Response object you can then modify?
- new_response()
- build_response()
- Response.create()
- make_response(...)
Answer: make_response(...). make_response(...) returns a Response whose status_code and headers you can set.
Why prefer request.form.get("x") over request.form["x"]?
- It returns None/default instead of a 400 when missing
- It is faster
- It validates the value
- It sends JSON
Answer: It returns None/default instead of a 400 when missing. .get() avoids a 400 Bad Request when the key is absent.
By default, what status code does a view returning a plain string send?
- 201 Created
- 200 OK
- 302 Found
- 204 No Content
Answer: 200 OK. A plain string response defaults to 200 OK.
What status results from POSTing to a route that only allows GET?
- 404 Not Found
- 500 Internal Error
- 405 Method Not Allowed
- 403 Forbidden
Answer: 405 Method Not Allowed. 405 Method Not Allowed means the verb isn't permitted on that route.
How do you let one route accept both GET and POST?
- @app.both_methods
- @app.post and @app.get only
- request.allow("POST")
- methods=["GET", "POST"] in @app.route
Answer: methods=["GET", "POST"] in @app.route. List the allowed methods in the @app.route decorator's methods argument.
Continue this course
- Previous: Static Files (CSS, JS, Images)
- Next: Handling Forms — Collect, validate, and process user input from HTML forms
- Quick reference: Flask cheat sheet › Request & Response
Frequently asked questions
What is the request object in Flask?
request is a global object Flask fills in for each incoming HTTP request. It exposes the query string (request.args), submitted form data (request.form), headers, JSON body, the HTTP method, and more, so your view function can read what the client sent.
What is the difference between request.args and request.form?
request.args holds query-string parameters from the URL (the part after the ?), typically from GET requests. request.form holds data submitted in the body of a POST request, usually from an HTML form. Both behave like dictionaries.
How do I return a custom status code or header?
Return a tuple of (body, status) or (body, status, headers), or build a Response with make_response(...) and set status_code and headers on it. This lets you send 201 Created, 404 Not Found, JSON, or custom headers explicitly.
How do I send JSON from a Flask route?
Use jsonify(...) which serializes a dict or list to JSON and sets the Content-Type header to application/json. Returning jsonify({'ok': True}) sends a proper JSON response the browser or an API client can parse.