GPTMap

OpenAI API Key Expiration: expires_in_seconds, Org Policies, and Safe Automation

Service Account API key expiry got its key upgrade in v3.11.0 / v7.13.0: org and project policies can now force expiration with a hard maximum lifetime. Parameter boundaries, code, and an ops checklist, all in one place.

TL;DR
openai-python v3.11.0 and openai-node v7.13.0 (2026-09-09) add org-level policy constraints to Service Account key expiry: expires_in_seconds takes 1..31536000 seconds (365 days); omitting it means no expiration unless a policy requires one; under a maximum-lifetime policy the value is mandatory and bounded. No policy endpoint appears in the SDK types as of 2026-09-10.
OpenAI API key expiration policy is the expiry semantics for Service Account API keys as of 2026-09-09: a service account can be created with expires_in_seconds (1 to 31536000 seconds) limiting the initial key's lifetime; an organization or project policy can require keys to expire and cap the maximum lifetime, in which case keys expire per policy even without an explicit value, and larger values are rejected.

How to

  1. Upgrade to an SDK with policy semantics

    Upgrade to openai>=3.11.0 (Python) or openai (openai-node)>=7.13.0. The basic expiration fields exist since v3.10.0 / v7.11.0; the policy-constrained semantics take effect in v3.11.0 / v7.13.0.

  2. Pass expires_in_seconds when creating the service account

    Call admin.organization.projects.serviceAccounts.create (or your client's equivalent) with name and expires_in_seconds (an integer between 1 and 31536000). If your organization or project has a maximum-lifetime policy, the value must not exceed it.

  3. Record expires_at from the response

    The response's expires_at is a Unix timestamp in seconds; null means the key does not expire. Put it in your key inventory and schedule rotation before the deadline.

  4. Handle rejected values

    Zero or negative values, values above 31536000, values above the policy limit, and combinations with create_service_account_only=true are rejected by schema validation — catch the 400 and surface the reason to the caller.

The expiry semantics of Service Account API keys graduated from "an optional field" to "policy governance" on 2026-09-09. openai-python v3.11.0 and openai-node v7.13.0 (both published around 15:30 UTC that day; release notes headlined "Add expiration controls for service account keys" / "Add API key expiration controls") gave expires_in_seconds hard bounds and organization-level policy constraints — "this key never expires" is no longer the final state of the default; it is now the temporary state of "no policy requires you to expire yet."

Every fact in this article comes from the two packages' release notes, PR diffs, and the v3.11.0 tag source re-fetched on 2026-09-10 (references at the end). OpenAI's documentation domains returned 403 to this site that day, so statements about console-side operations stay within what the type layer shows.

1. Overview: what changed

The OpenAI API key expiration policy is the Service Account key expiry semantics as of 2026-09-09: create a service account with expires_in_seconds (1 to 31536000 seconds) to bound the initial key's lifetime, and an organization or project policy can require expiration with a maximum lifetime.

The timeline has two steps that are easy to conflate:

VersionPublished (UTC)Capability delivered
openai-python v3.10.0 / openai-node v7.11.02026-09-08 / 09-08Basic expiration fields: expires_in_seconds (request) + expires_in_seconds / expires_at (response)
openai-python v3.11.0 / openai-node v7.13.02026-09-09 15:30Policy-constrained semantics: org/project policies can force expiration; bounds validation (1..31536000); the create_service_account_only conflict

Step one was "you can pass this field." Step two is "your organization can force you to pass it — and cap how large it can be." The two packages' version numbers do not map one-to-one, which is normal for OpenAI's dual SDKs.

2. The full semantics of expires_in_seconds

The docstring in v3.11.0's ServiceAccountCreateParams (src/openai/types/admin/organization/projects/service_account_create_params.py) deserves to be read in full:

Number of seconds until the initial API key expires. If omitted or null, the key does not expire unless the effective organization or project policy requires an expiration. When a policy sets a maximum lifetime, this value must be provided and must not exceed that limit. A non-null value cannot be used when create_service_account_only is true.

Broken into four rules:

  1. Explicit value: the initial key expires after expires_in_seconds seconds; valid range is 1 to 31536000 (365 days) — schema-enforced (minimum: 1 / maximum: 31536000), anything outside is rejected.
  2. Omitted or null: the key does not expire — but only when no effective organization/project policy requires expiration. With a policy in place, the key expires per policy even though you passed nothing.
  3. Policy with a maximum lifetime: expires_in_seconds flips from optional to mandatory, and the value must not exceed the policy limit.
  4. Mutually exclusive with create_service_account_only: create_service_account_only: true creates a service account with no initial key — there is no initial key to expire, so a non-null value is rejected.

One-sentence summary: the policy is the new highest priority. Whether or not your code passes a value, it first has to answer "does my organization have a policy, and what is its cap?"

3. The org/project policy: what the type layer confirms and what it does not

Confirmed (type-layer evidence): the policy's effects — it can expire keys created with no explicit value, cap expires_in_seconds, and turn the optional into the mandatory.

Not confirmed (no type-layer evidence, as verified on 2026-09-10): the policy's management endpoints. Neither SDK ships resources or methods for creating, querying, or modifying policies. Based on the available evidence, policies are set through the OpenAI admin console; the types show no counter-evidence and no evidence of an API. When an endpoint appears in the type layer, this site will follow up.

For multi-team organizations this is a classic platform-governance signal: a security team can mandate "every service account key lives at most N days" at the org level, and application code gets constrained without changing a line — over-limit values are rejected, and value-less keys expire per policy.

4. Code

4.1 Creating a Service Account with an expiring key (Python)

from openai import OpenAI

client = OpenAI()

sa = client.admin.organization.projects.service_accounts.create(
    project_id="prj_xxx",              # placeholder
    name="ci-runner",
    expires_in_seconds=60 * 60 * 24 * 90,   # 90 days (7,776,000 s; valid range 1..31536000)
)
print(sa.expires_at)   # Unix timestamp (seconds); null = never expires

4.2 The same call in Node

import OpenAI from 'openai';

const client = new OpenAI();

const sa = await client.admin.organization.projects.serviceAccounts.create({
  project_id: 'prj_xxx',        // placeholder
  name: 'ci-runner',
  expires_in_seconds: 60 * 60 * 24 * 90,
});
console.log(sa.expires_at);     // Unix timestamp (seconds); null = never expires

4.3 Auditing existing keys' expiration (Python)

keys = client.admin.organization.projects.api_keys.list(project_id="prj_xxx")
for k in keys.data:
    print(k.id, k.expires_at)   # project API key objects carry expires_at as of v3.11.0

All three snippets are SDK-source-verified (field-by-field against the v3.11.0 type definitions) and were not executed against a live key; replace placeholder IDs such as project_id with your own resources.

5. Ops checklist: putting policy semantics to work

  • CI and automation: align key lifetime with deployment cadence — pass 30 days (2,592,000 seconds) for pipeline keys with a rotation calendar. Do not treat the 365-day cap as a target; it is a ceiling.
  • Key inventory: record every key's expires_at and monitor deadlines. Legacy keys with null expires_at will not expire until your organization sets a policy — and their behavior will change when you do, so inventory first.
  • Error handling: zero, negative, above 31536000, above the policy limit, or combined with create_service_account_only: true are all schema-rejected. Catch the 400-class errors and surface the reason instead of letting it become a 3 a.m. page.
  • Upgrade order: upgrade the SDK (Python ≥ v3.11.0 / Node ≥ v7.13.0) before touching key strategy — older types lack the policy semantics, and validation error messages will not line up.

6. Common pitfalls

  • Passing 0 or a negative number: the schema requires minimum: 1 and rejects it outright. "Expire immediately" is not what this parameter does — it sets lifetime at creation, it is not a revocation tool.
  • Exceeding 31536000: 365 days is a hard ceiling. "Expire in two years" gets rejected; staged rotation is the right answer.
  • Combining with create_service_account_only: mutually exclusive. Omit expires_in_seconds when creating a key-less service account.
  • Assuming this covers user keys: as verified on 2026-09-10, expiration fields exist only on Service Account keys and the project API key object; the type layer shows no corresponding change for user API keys.
  • Policy-limit errors that do not match the docs: the policy limit is organization-specific and invisible in the SDK types — the cap in an error message reflects your organization's configuration, which is exactly why your code must be able to read policy from the error.

7. Next steps

Key points

  • Valid range for expires_in_seconds: 1 to 31536000 seconds (365 days), enforced by schema validation, since 2026-09-09 (openai-python v3.11.0 / openai-node v7.13.0)
  • The default changed in meaning: omitted or null = the key does not expire — but only when no organization/project policy requires expiration; with a policy in place, keys expire per policy
  • When a policy sets a maximum lifetime: expires_in_seconds becomes mandatory and must not exceed the policy limit
  • When create_service_account_only is true (creating a service account without an initial key), a non-null expires_in_seconds is rejected
  • Response side: both the project API key object and the service account creation response carry expires_at (Unix timestamp in seconds; null = never expires)
  • As verified on 2026-09-10: no endpoint for creating or querying policies appears in either SDK's types — per the type-layer evidence, policies are set through the admin console

Frequently asked questions

31536000 seconds — 365 days. This is a hard schema validation (minimum: 1, maximum: 31536000) as of v3.11.0; larger values are rejected. It is a cap, not a recommendation.

Official references

Related articles

Subscribe to GPTMap Weekly

One email every Monday: curated OpenAI updates, deep dives, and best practices. No ads, unsubscribe anytime.

Submitting opens Buttondown in a new tab to confirm your subscription.

GPTMap EditorialPublished 2026-09-10 7 min read
Test environment (EEAT)
Last tested: 2026-09-10
Model used: gpt-5.6 (current flagship family; Admin API key governance is independent of model version)