How to Use a Configuration File in Python
Learn to read, validate, override, and write INI, TOML, and JSON configuration in Python, with runnable examples and fixes for common errors.
Use Python’s built-in configparser to read and write sectioned INI-style configuration files. Use tomllib for TOML input on Python 3.11 and later, or json when your settings are already JSON. The examples below show how to load required and optional files, convert values to the types your program needs, apply overrides, and handle common errors.
1. Choose a configuration format
| Format | Standard library | Use it when | Consider |
|---|---|---|---|
| INI-style | configparser |
You want sections and built-in reading and writing. | Values are strings until converted. Writing parsed settings does not preserve comments. |
| TOML | tomllib |
You want TOML input and its typed values. | Available in Python 3.11 and later; reads TOML but does not write it. |
| JSON | json |
Your application or another system already uses JSON. | JSON does not support comments. |
For a new, sectioned settings file that your Python program may also write, start with configparser. If the file already uses another format, keep that format unless you have a reason to migrate. See the Python documentation for configparser, tomllib, and json.
2. Read an INI file with configparser
Create settings.ini beside your script:
[server]
host = localhost
port = 8080
debug = false
Save this runnable program as app.py in the same directory:
import configparser
from pathlib import Path
config_path = Path(__file__).with_name("settings.ini")
config = configparser.ConfigParser()
# read() returns the paths it successfully opened and ignores missing files.
loaded = config.read(config_path, encoding="utf-8")
if not loaded:
raise FileNotFoundError(f"Could not read configuration: {config_path}")
server = config["server"]
host = server["host"]
port = server.getint("port", fallback=8080)
debug = server.getboolean("debug", fallback=False)
print(f"Connecting to {host}:{port}; debug={debug}")
Run it with python app.py. Path(__file__).with_name(...) resolves the settings path relative to the script rather than depending on the shell’s current directory.
Require the file explicitly
When absence of a configuration file should be an error, use read_file(). It raises an error if it cannot open the file, instead of silently skipping it:
import configparser
from pathlib import Path
config_path = Path(__file__).with_name("settings.ini")
config = configparser.ConfigParser()
with config_path.open(encoding="utf-8") as file:
config.read_file(file, source=str(config_path))
Use defaults and sections
The special [DEFAULT] section supplies values that other sections can inherit. A section-specific value takes precedence over the default for that option:
[DEFAULT]
timeout = 30
[server]
host = localhost
port = 8080
timeout = config["server"].getint("timeout", fallback=10)
Here, the value from [DEFAULT] is used, so timeout is 30. The fallback applies only if the option is absent from both the section and its defaults.
3. Convert values and validate settings
INI values are strings. Use typed getters for common types so invalid input raises an error rather than being mistaken for a usable value:
port = config["server"].getint("port", fallback=8080)
ratio = config["server"].getfloat("ratio", fallback=1.0)
debug = config["server"].getboolean("debug", fallback=False)
For example, getboolean() recognizes standard boolean spellings such as yes, no, true, false, on, off, 1, and 0, ignoring case. If a required value is missing, direct access such as config["server"]["host"] raises an error. If a value has the wrong type, a typed getter raises a conversion error. Both behaviors are useful for catching a bad configuration early.
try:
server = config["server"]
host = server["host"]
port = server.getint("port")
except KeyError as exc:
raise ValueError(f"Missing configuration value: {exc}") from exc
except ValueError as exc:
raise ValueError("The server port must be an integer") from exc
if not 1 <= port <= 65535:
raise ValueError("The server port must be between 1 and 65535")
4. Layer configuration files predictably
Pass multiple paths to read() to combine a base file with optional overrides. Later files replace conflicting values; options that appear only in an earlier file remain available:
config = configparser.ConfigParser()
loaded = config.read(
["settings.ini", "settings.local.ini"],
encoding="utf-8",
)
print("Files loaded:", loaded)
In this example, settings.local.ini wins when both files define the same option. Missing paths are skipped by read(), so inspect the returned list if you need to know what was loaded. For a required base file, open it and use read_file() first, then use read() for optional overrides.
Write down the precedence your application uses, such as base file first and local override second. The values returned by the parser then follow that order. This example does not load environment variables; if your program also supports them, implement and document that precedence separately.
5. Write an INI configuration file
Create or update values in a parser, then write it to a text file:
import configparser
from pathlib import Path
config = configparser.ConfigParser()
config["server"] = {"host": "localhost", "port": "8080"}
config["DEFAULT"] = {"timeout": "30"}
output_path = Path("settings.ini")
with output_path.open("w", encoding="utf-8") as file:
config.write(file)
Writing parsed configuration serializes the settings, but does not retain comments from the original file. If preserving comments or formatting matters, account for that before using a read-and-write parser workflow.
6. Load TOML in Python
Python 3.11 and later includes tomllib, which parses TOML 1.0.0. Open TOML in binary mode and pass the file object to load(). Given a file named settings.toml containing host = "localhost" and port = 8080, this program reads its values:
import tomllib
from pathlib import Path
config_path = Path(__file__).with_name("settings.toml")
with config_path.open("rb") as file:
config = tomllib.load(file)
host = config["host"]
port = config["port"]
print(f"Connecting to {host}:{port}")
TOML values are parsed into Python values, so an integer such as 8080 is an integer in the result. tomllib is read-only; it does not provide a TOML writer. If your application needs to write TOML or edit it while preserving style, the Python documentation points to third-party packages for those needs.
For TOML from an untrusted source, limit the amount of input your application accepts. The standard library documentation warns that malicious input can consume considerable CPU and memory while parsing.
7. Load JSON configuration in Python
Use json.load() for a JSON file. For example, settings.json can contain:
{
"server": {
"host": "localhost",
"port": 8080
}
}
Load and access it as a dictionary:
import json
from pathlib import Path
config_path = Path(__file__).with_name("settings.json")
with config_path.open(encoding="utf-8") as file:
config = json.load(file)
host = config["server"]["host"]
port = config["server"]["port"]
print(f"Connecting to {host}:{port}")
JSON has no comment syntax. Remove comments from the file and ensure strings and property names use double quotes if parsing fails.
8. Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| A required setting file appears to load, but no values exist. | ConfigParser.read() skips files it cannot open. |
Check its returned filename list, resolve the path deliberately, or open the required file and call read_file(). |
KeyError for a section or option. |
The name is missing, misspelled, or placed under a different section. | Check the file’s section and option names; decide whether the setting is required or should have a fallback. |
| A port or flag cannot be converted. | The INI value is not in a form accepted by the typed getter. | Correct the value and use getint(), getfloat(), or getboolean() to report invalid input clearly. |
| Unexpected value wins after loading several files. | A later file overrides a value from an earlier file. | Check the ordered list passed to read() and document which file has priority. |
| A setting name’s capitalization changes. | Option names are case-insensitive and lowercased by default. | Use lowercase option names, or set config.optionxform if case-sensitive option names are a requirement. |
| A value containing percent signs causes interpolation errors. | ConfigParser interpolation interprets percent-based substitutions by default. |
Use raw=True for a raw read, or construct the parser with interpolation=None when interpolation is not wanted. |
| Comments disappear after saving. | ConfigParser.write() writes parsed settings, not the original comments. |
Keep comments in a separate template or choose a tool suited to preserving file style. |
ModuleNotFoundError: No module named 'tomllib'. |
The interpreter is older than Python 3.11. | Use Python 3.11 or newer for standard-library tomllib; for older interpreters, consult the TOML package options referenced by Python’s documentation. |
| TOML parsing fails. | The file is invalid TOML, opened in text mode, or has a missing key later in the program. | Open in binary mode, correct the TOML syntax, and validate expected tables and keys before use. |
| JSON parsing fails near a comment or trailing comma. | Those are not valid JSON syntax. | Remove comments and trailing commas, then ensure the document is valid JSON. |
9. Performance, reliability, and cost
For ordinary application settings, configuration loading is usually part of startup: parse once, validate once, and pass the resulting values to the parts of the program that need them. Re-reading files repeatedly adds I/O and makes it harder to reason about whether different parts of a running program see the same settings. If your application must support live edits, define when it reloads and how it handles an invalid replacement.
Use an explicit text encoding such as UTF-8 for INI and JSON files. Resolve paths relative to a known location rather than relying on the process working directory. Treat missing required files, missing keys, and malformed types as actionable configuration errors. No paid service is required for these standard-library approaches; costs are the time to maintain the file format and any operational work your application adds.
10. Or skip the browser setup
If your Python application also needs website screenshots, ScreenshotNeo is a website screenshot API and MCP server for developers. Its one-request API returns a screenshot or PDF. See the ScreenshotNeo API documentation.
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Cookie banners are accepted and removed before capture, along with known newsletter popups and chat widgets. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. An MCP server gives AI agents screenshot, page-info, and PDF tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up for ScreenshotNeo’s free plan to get 1,000 screenshots a month with no card.
11. Frequently asked questions
How do I read a config file in Python?
For INI, create a ConfigParser and call read() or read_file(). Choose the loader that matches the file’s format for TOML or JSON.
How do I use configparser?
Read the file into configparser.ConfigParser(), access values by section and option, and use typed getters when a value should be an integer, float, or boolean.
Can I use TOML without installing a package?
Yes, if you use Python 3.11 or later: tomllib is part of the standard library and reads TOML files.
Can configparser preserve comments?
No. Writing parsed settings with ConfigParser.write() does not preserve the original comments.
Which format should I pick for a new project?
Use INI when you want straightforward sectioned settings and standard-library read/write support. Choose TOML or JSON when your project’s existing tools and interfaces already use those formats.


