import time import jwt # PyJWT; ES256 needs the cryptography package: pip install "pyjwt[crypto]==2.15.1" with open("key.pem") as f: private_key = f.read() # PEM text; PyJWT loads it with cryptography now = int(time.time()) # whole seconds, as NumericDate expectstoken = jwt.encode( { "iss": "https://issuer.example.com", "sub": "user-42", "aud": "api://orders", "iat": now, "exp": now + 15 * 60, }, private_key, algorithm="ES256", headers={"kid": "2026-10"},)print(token) How it works
jwt.encodeaccepts the PEM text directly (PKCS#8 orEC PRIVATE KEY) and loads it with cryptography.headers={"kid": "2026-10"}adds the key id; PyJWT always writesalgandtypitself.iatandexpare integers here; adatetimealso works and is converted to whole seconds.- PyJWT 2.x returns the token as
str(1.x returnedbytes), so it can be printed or put in a header as is.
Gotchas
- Plain
pip install pyjwtcannot sign ES256:NotImplementedError: Algorithm 'ES256' could not be found. Do you have cryptography installed?. Installpyjwt[crypto]. - The key must match the algorithm: a P-384 key with
algorithm="ES256"raisesInvalidKeyError: The key's curve 'secp384r1' does not match the expected curve 'secp256r1' for this algorithm. - Passing the public key, or the PEM as a secret for
HS256, is a different mistake with a different error — see the related error pages; ES256 always signs with the private key. jwt.encodedoes not addexporiaton its own. A token withoutexpnever expires, and most verifiers accept it unless told to require the claim.