How to Write JSON Data to a File in Python

To write JSON data to a file in Python, open the file in write mode and call json.dump(data, f). Add indent=2 for readable output, and open the file with encoding="utf-8" so names with accents survive:

with open("orders.json", "w", encoding="utf-8") as f:
    json.dump(orders, f, indent=2, ensure_ascii=False)

These snippets ran on Python 3.12.5 in a Windows Command Prompt, where encoding mistakes show up fast.

You’ll see json.dump with a list of records, the ensure_ascii setting, the datetime TypeError and its fix, appending to a JSON file, and writing without corrupting the file if something fails.

Write JSON to a file with json.dump()

json.dump() writes straight to a file object; json.dumps() returns a string instead. Here a list of order dictionaries goes to disk and is read back:

import json
from pathlib import Path

orders = [
    {"id": 1001, "customer": "Emily Carter", "city": "Austin", "total": 129.99, "paid": True},
    {"id": 1002, "customer": "José Ramírez", "city": "San Antonio", "total": 54.5, "paid": False},
]

with open("orders.json", "w", encoding="utf-8") as f:
    json.dump(orders, f, indent=2, ensure_ascii=False)

print(Path("orders.json").read_text(encoding="utf-8"))
print(json.load(open("orders.json", encoding="utf-8"))[1]["customer"])

Output:

[
  {
    "id": 1001,
    "customer": "Emily Carter",
    "city": "Austin",
    "total": 129.99,
    "paid": true
  },
  {
    "id": 1002,
    "customer": "José Ramírez",
    "city": "San Antonio",
    "total": 54.5,
    "paid": false
  }
]
José Ramírez
Command Prompt output of Python json.dump writing a list of orders to orders.json with indent 2, then reading it back
The file holds both orders indented by two spaces, True and False become true and false, and José Ramírez reads back intact.

Python’s True becomes true and None becomes null. The mapping goes the other way too, as shown in converting a Python dict to JSON.

Save a Python dictionary as a JSON file and load it back

A single dict works the same way. The surprise comes when you read it back: JSON object keys are always strings, so integer keys don’t round-trip:

import json
from pathlib import Path

settings = {"store": "Austin #12", "tax_rate": 0.0825, "open_days": ["Mon", "Tue", "Wed"],
            1001: "Emily Carter", 1002: "James Wilson"}          # int keys

Path("settings.json").write_text(json.dumps(settings, indent=2, sort_keys=False), encoding="utf-8")
loaded = json.loads(Path("settings.json").read_text(encoding="utf-8"))

print(loaded == settings, list(loaded)[-2:])        # int keys came back as strings
fixed = {int(k) if k.isdigit() else k: v for k, v in loaded.items()}
print(fixed == settings)

try:
    json.dumps({("TX", "Austin"): 12})
except TypeError as err:
    print("TypeError:", err)

Output:

False ['1001', '1002']
True
TypeError: keys must be str, int, float, bool or None, not tuple
Command Prompt output of Python saving a dictionary as JSON and loading it back, showing integer keys returned as strings and a tuple key TypeError
The loaded dict doesn’t equal the original because 1001 and 1002 come back as strings; converting them fixes it, and a tuple key raises TypeError.

Tuple keys can’t be saved at all. Convert them to strings first, or store a list of records instead. Reading JSON text into a dict is covered in converting a JSON string to a dictionary.

Keep accented characters readable with ensure_ascii

By default, json escapes every non-ASCII character. That’s valid JSON, but hard to read:

import json

row = {"customer": "José Ramírez", "note": "Café ☕"}
print(json.dumps(row))                       # default: ensure_ascii=True
print(json.dumps(row, ensure_ascii=False))   # readable, needs a UTF-8 file
print(json.dumps(row, separators=(",", ":"), ensure_ascii=False))   # compact

Output:

{"customer": "Jos\u00e9 Ram\u00edrez", "note": "Caf\u00e9 \u2615"}
{"customer": "José Ramírez", "note": "Café ☕"}
{"customer":"José Ramírez","note":"Café ☕"}

With ensure_ascii=False you must write the file as UTF-8, or Windows may fall back to another code page. For spacing and sorting options, see pretty printing JSON in Python.

Why do I get “Object of type datetime is not JSON serializable”?

JSON only knows strings, numbers, booleans, null, arrays and objects. A datetime, Decimal or set stops json.dump():

import json
from datetime import datetime

order = {"id": 1003, "placed": datetime(2026, 10, 6, 9, 30), "total": 89.0}

try:
    with open("order.json", "w", encoding="utf-8") as f:
        json.dump(order, f)
except TypeError as err:
    print("TypeError:", err)

print("left behind:", repr(open("order.json", encoding="utf-8").read()))

Output:

TypeError: Object of type datetime is not JSON serializable
left behind: '{"id": 1003, "placed": '
Command Prompt output of Python TypeError Object of type datetime is not JSON serializable from json.dump
json.dump raises TypeError: Object of type datetime is not JSON serializable, and part of the file is already written.

Pass a default function. json calls it for anything it can’t handle, and you return something it can:

import json
from datetime import date, datetime
from decimal import Decimal
from pathlib import Path

def to_json(value):
    if isinstance(value, (datetime, date)):
        return value.isoformat()
    if isinstance(value, Decimal):
        return str(value)            # keep every cent exactly
    if isinstance(value, set):
        return sorted(value)
    raise TypeError(f"Can't serialize {type(value).__name__}")

order = {"id": 1003, "placed": datetime(2026, 10, 6, 9, 30), "ship_by": date(2026, 10, 9),
         "total": Decimal("89.10"), "tags": {"gift", "express"}}

with open("order.json", "w", encoding="utf-8") as f:
    json.dump(order, f, indent=2, default=to_json)

print(Path("order.json").read_text(encoding="utf-8"))

Output:

{
  "id": 1003,
  "placed": "2026-10-06T09:30:00",
  "ship_by": "2026-10-09",
  "total": "89.10",
  "tags": [
    "express",
    "gift"
  ]
}
Command Prompt output of Python json.dump with a default function turning datetime, date, Decimal and set into JSON values
The dates are written as ISO strings, the Decimal total as “89.10”, and the tags as a sorted list.

The failed run above left a partial order.json behind, since the file was opened before the error. The safe-write pattern below avoids that. Before storing timestamps, read naive vs aware datetime in Python so the ISO strings carry a time zone when they need one.

Append data to a JSON file

A JSON file holds one value, so you can’t just append to it. Either load it, change it and rewrite it, or switch to JSON Lines: one JSON object per line, which works with append mode:

import json
from pathlib import Path

Path("events.jsonl").unlink(missing_ok=True)

def log_event(event):
    with open("events.jsonl", "a", encoding="utf-8") as f:      # append one line per record
        f.write(json.dumps(event) + "\n")

log_event({"user": "olivia", "action": "login", "at": "2026-10-06T08:01:00"})
log_event({"user": "james", "action": "checkout", "amount": 42.75})
log_event({"user": "olivia", "action": "logout"})

print(Path("events.jsonl").read_text(encoding="utf-8"))
events = [json.loads(line) for line in open("events.jsonl", encoding="utf-8")]
print(len(events), "events; checkouts:", sum(e.get("amount", 0) for e in events))

Output:

{"user": "olivia", "action": "login", "at": "2026-10-06T08:01:00"}
{"user": "james", "action": "checkout", "amount": 42.75}
{"user": "olivia", "action": "logout"}

3 events; checkouts: 42.75
Command Prompt output of Python appending events to a JSON Lines file and reading them back line by line
Three events land on three lines, and reading them back finds three events with one checkout amount.

I use JSON Lines for logs and exports that grow over time. If you’re writing other text formats, see writing a list to a file in Python.

Write a JSON file safely with os.replace()

If dump() fails halfway, the old file is already truncated. Write to a temporary file in the same folder, then swap it in with os.replace():

import json
import os
import tempfile
from pathlib import Path

def write_json_atomic(path, data):
    path = Path(path)
    fd, tmp = tempfile.mkstemp(dir=path.parent, suffix=".tmp")
    try:
        with os.fdopen(fd, "w", encoding="utf-8") as f:
            json.dump(data, f, indent=2)
            f.flush()
            os.fsync(f.fileno())
        os.replace(tmp, path)          # swap in one step
    except BaseException:
        os.remove(tmp)
        raise

write_json_atomic("settings.json", {"theme": "dark", "currency": "USD"})
try:
    write_json_atomic("settings.json", {"theme": "light", "bad": {1, 2}})
except TypeError as err:
    print("write failed:", err)
print("file still holds:", Path("settings.json").read_text(encoding="utf-8").replace("\n", " "))

Output:

write failed: Object of type set is not JSON serializable
file still holds: {   "theme": "dark",   "currency": "USD" }

The second write fails on the set, and the original settings survive untouched. To replace an existing file on purpose, see overwriting a file in Python.

JSON writing options compared

NeedCode
Write to a filejson.dump(data, f)
Get a stringjson.dumps(data)
Readable fileindent=2, ensure_ascii=False + encoding="utf-8"
datetime / Decimal / setdefault=to_json
Add records over timeJSON Lines with mode "a"
Never leave a broken fileTemp file + os.replace()

Working with JSON regularly? These guides are next: