Send JSON from Flask APIs

Send JSON responses from Flask APIs — return JSON with jsonify, handle status codes, and test with curl. Step 7 Python web development tutorial.

Focus: send json responses from flask apis

Sponsored

You’ve got a Flask route that returns a string, but your frontend or mobile app is waiting for data — and all it gets is text that breaks JSON.parse(). If you’re building any modern API, sending clean, structured JSON responses is non-negotiable. In this lesson, you’ll learn the right way to return JSON from Flask — using jsonify, setting proper HTTP status codes, and testing your endpoints with curl — so your API behaves like a professional service, not a college project.

The problem this lesson solves

Every day, developers hit the same wall: they write a Flask route that returns a Python dict, and Flask converts it to a string using str(). The response looks like {'message': 'hello'} — which is not valid JSON. Quotes are single instead of double, and the frontend can’t parse it.

Even worse, many tutorials show you can return a dict directly from a Flask route, and Flask will auto-serialize it. That works for simple cases, but it hides important details: you can’t easily set custom status codes or headers, and the JSON isn’t guaranteed to be properly formatted with correct content-type. This lesson solves those problems by teaching you the standard, robust way to send JSON responses from Flask APIs — using jsonify.

Core concept / mental model

Think of your Flask API as a waiter in a restaurant. The client is the customer. They order a dish (send an HTTP request). The kitchen (your Python code) prepares the food (processes the request). But the waiter can’t just throw raw ingredients on the table — they must plate the dish nicely on a tray with the right utensils. jsonify is that tray: it takes your Python data and wraps it in the correct HTTP response with the right Content-Type header (application/json) and proper JSON formatting.

In more technical terms, a response from a Flask route has three parts: the body (the actual content), the status code (e.g., 200 for success, 404 for not found), and headers (metadata like content type). jsonify handles all three elegantly. Without it, you’re just throwing a Python dict at the client and hoping it sticks.

How it works step by step

Step 1: Import jsonify

Your Flask app already imports Flask. Add jsonify to that import:

from flask import Flask, jsonify

Step 2: Return a dict or list with jsonify

Inside a view function, pass the Python object you want to send — a dict, a list, or even a custom object — to jsonify(). Flask will serialize it to JSON with proper escaping and set the Content-Type header.

Step 3: Set a custom status code (optional)

You can pass an integer status code as a second argument to jsonify(), or return a tuple (response, status_code). This lets you send 201 Created, 400 Bad Request, 404 Not Found, etc.

Step 4: Return the response

The jsonify() call returns a Response object. Return it directly from the view function — Flask handles the rest.

Hands-on walkthrough

Let’s build a tiny Flask API from scratch. Create a file called app.py:

from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/')
def home():
    return jsonify(message="Hello, World!")

if __name__ == '__main__':
    app.run(debug=True)

Run the app with python app.py, then test with curl:

curl http://127.0.0.1:5000/

Expected output:

{
  "message": "Hello, World!"
}

And the response header Content-Type will be application/json — check with curl -i to verify.

Now, let’s send more complex data with status codes. Here’s a full example that simulates a user API:

from flask import Flask, jsonify

app = Flask(__name__)

# Sample data
users = [
    {"id": 1, "name": "Alice", "email": "alice@example.com"},
    {"id": 2, "name": "Bob", "email": "bob@example.com"}
]

@app.route('/api/users')
def get_users():
    return jsonify(users)

@app.route('/api/users/<int:user_id>')
def get_user(user_id):
    user = next((u for u in users if u["id"] == user_id), None)
    if user is None:
        return jsonify({"error": "User not found"}), 404
    return jsonify(user)

if __name__ == '__main__':
    app.run(debug=True)

Test it:

# Get all users
curl http://127.0.0.1:5000/api/users

# Get a user by ID
curl http://127.0.0.1:5000/api/users/1

# Try a non-existent user
curl -i http://127.0.0.1:5000/api/users/999

Expected output for the 404 case includes HTTP/1.1 404 NOT FOUND in the response headers and a JSON body with the error message.

Compare options / when to choose what

Approach How it works Pros Cons Best for
flask.jsonify() Converts Python data to JSON, sets headers Simplest, handles formatting, easy status codes Tied to Flask request context (not needed for basic use) Most Flask API responses
Return a dict directly Flask auto-converts, no extra import Minimal code No custom status codes, header magic invisible Quick prototypes, small scripts
Manual json.dumps() You serialize and build a Response yourself Full control over JSON options and headers More verbose, easy to forget headers Advanced cases needing custom JSON encoders

Pro tip: Always use jsonify unless you have a specific reason not to. It follows Flask conventions and keeps your code readable for other Flask devs.

Troubleshooting & edge cases

Error: TypeError: Object of type set is not JSON serializable

Sets aren’t valid JSON. Convert them to a list first: jsonify(list(my_set)).

Error: Response looks like {'key': 'value'} (single quotes)

That’s Python’s repr(), not JSON. You forgot to use jsonify — you’re returning a raw dict, and Flask’s auto-conversion isn’t happening because you maybe used a different response type. Double-check your import and call.

Status code isn’t sticking

If you return jsonify(data) and then separately set return response, 404, you might be overwriting the response. Use the tuple form: return jsonify(data), 404.

Dictionaries with non-string keys

JSON keys must be strings. If you have {1: "one"}, jsonify will raise a TypeError. Cast keys to strings manually.

What you learned & what's next

You learned to send JSON responses from Flask APIs using jsonify, set custom status codes, test endpoints with curl, and avoid common serialization pitfalls. This is the foundation of any web API — now your routes can speak JSON fluently.

Next in the track, you’ll learn to receive JSON data from clients — parsing request bodies with request.get_json() — so your API can accept and process input, not just output it. That’s where the real interactivity begins.

Keep building!

Practice recap

Create a new Flask app with three routes: /status returns a JSON health check with status 200, /items returns a list of items, and /item/<int:item_id> returns a single item or a 404 error JSON. Test all three with curl, and inspect the headers to confirm Content-Type: application/json.

Common mistakes

  • Forgetting to import jsonify from flask, so you return a raw dict and get single-quoted Python repr instead of JSON.
  • Returning a set, datetime, or custom object without converting — jsonify can't serialize them; convert to list/string first.
  • Trying to set a status code with return jsonify(...), 404 but using a variable that gets overwritten, or returning only the data with no status tuple.
  • Testing with a browser that renders JSON differently, making you think something's wrong when it's fine — use curl with -i to inspect headers.

Variations

  1. Use flask.Response with json.dumps() for full control over JSON encoding and headers.
  2. Use Flask-RESTful or Flask-RESTX libraries that wrap JSON responses and add built-in request parsing.
  3. Return JSON from a generator or stream for large datasets using stream_with_context and manual serialization.

Real-world use cases

  • A RESTful API endpoint for a to-do list app that returns tasks as JSON to a JavaScript frontend.
  • A microservice that exposes user profile data in JSON for an internal dashboard built with React.
  • A public weather API that returns forecast data as JSON for third-party clients to consume.

Key takeaways

  • Use jsonify() to send JSON responses — it sets the correct Content-Type and handles serialization.
  • Set custom HTTP status codes by passing a tuple: return jsonify(data), 404.
  • Test API endpoints with curl -i to inspect both status code and headers, not just the body.
  • Convert non-serializable types like sets and datetime objects before using jsonify().
  • Choose plain dicts for quick prototypes, but jsonify() for production-quality APIs.
  • Next step: learn to parse incoming JSON request data to build fully interactive APIs.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.