Using PyPI API Tokens with Twine for Secure Package Publishing
Learn how to replace your PyPI password with a scoped API token for Twine, enabling safe automated package publishing in CI pipelines.
01 Jun 2026, 15:19 UTC

The problem: exposing your PyPI password in CI
When you automate package publishing with twine upload in a CI pipeline, the simplest approach is to put your PyPI username and password in a script or environment variable. If that password is your main account credential, any leak gives an attacker full control over every package you own.
Thesis: use a scoped API token instead of your password
PyPI lets you generate an API token that can be used as the password for Twine. The token can be limited to specific projects or left with the same broad scope as your account, but it is a separate secret that you can revoke without changing your main password.
Creating and storing the token
- Log in to pypi.org, go to Account Settings → API tokens, and click “Add API token”.
- Give the token a descriptive name (e.g., “ci‑publish”) and optionally restrict it to specific projects.
- Copy the token value; it looks like
pypi-AgEIcH....
Store the token where your CI system can read it, but never in plain text in the repository. Two common patterns:
- As environment variables:
TWINE_USERNAME=__token__andTWINE_PASSWORD=<your‑token>. - In a
.pypircfile with[pypi]section:[pypi] username = __token__ password = pypi-AgEIcH...
Both approaches keep the secret out of your source code.
Using the token with Twine
From a local machine or a CI job, run:
twine upload dist/*
Twine reads the credentials from the environment or .pypirc and sends the token as the password. If the token is valid and has permission to upload the project, the command finishes with a success message and the new version appears on pypi.org.
Trade‑off and limitation
The token inherits the same scope as your PyPI account unless you explicitly limit it. A token created without project restrictions can upload or delete any package you own, so treat it as a privileged credential. Tokens are also long‑lived by default; you must rotate them periodically and update any CI configuration that relies on them.
Practical verification
- Upload a test package with the token and confirm the new version appears on PyPI.
- Revoke the token from the Account Settings → API tokens page.
- Run
twine upload dist/*again; the command should fail with an authentication error (e.g.,401 UnauthorizedorInvalid or non‑existent authentication token). - If the failure occurs, you have verified that the token was required and that revocation works.
Actionable closing
Generate a scoped API token for each CI service or team, store it as an environment variable or in a protected .pypirc, and use twine upload exactly as you would with a password. Rotate the token every few months and immediately revoke it if you suspect exposure. This gives you the convenience of automated publishing without sacrificing the security of your main PyPI account.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.