Quilt supports two authentication methods for accessing your data:
Interactive authentication uses OAuth or SSO to authenticate through your web browser. This is the recommended method for personal use, Jupyter notebooks, and local development.
import quilt3
# Opens your browser for authentication
quilt3.login()
This command will:
# Returns catalog URL if authenticated, None otherwise
catalog_url = quilt3.logged_in()
if catalog_url:
print(f"Authenticated to: {catalog_url}")
else:
print("Not authenticated")
# Clear all credentials (both interactive and API keys)
quilt3.logout()
NOTE: This feature requires Quilt Platform version 1.67 or higher
API keys provide programmatic access to Quilt without requiring browser-based authentication. Keys are created through the Python API and can be used for automated workflows.
First, log in using the interactive method:
import quilt3
quilt3.login()
# Create a key that expires in 90 days (default)
key, secret = quilt3.api_keys.create("my-automation-key")
# Or specify a custom expiration (1-365 days)
key, secret = quilt3.api_keys.create("my-key", expires_in_days=180)
# IMPORTANT: Save the secret - it's only shown once!
print(f"Key ID: {key.id}")
print(f"Owner: {key.user_email}")
print(f"Secret: {secret}")
print(f"Fingerprint: {key.fingerprint}")
print(f"Expires: {key.expires_at}")
# Example output:
# Secret: qk_1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t1u2v3w4x5y6z...
⚠️ Security Warning: The secret is only shown once during creation. Save it immediately in a secure location. If you lose it, you’ll need to create a new key.
Choose a secure storage method based on your environment:
Create a .env file in your project directory:
# .env
QUILT_REGISTRY_URL=https://example-registry.quiltdata.com
QUILT_API_KEY=qk_your_secret_here
Use the registryUrl value from your catalog’s /config.json, not the catalog
homepage URL. These URLs can differ.
Load it in your Python code:
from dotenv import load_dotenv
import os
load_dotenv() # Load .env file
api_key = os.environ["QUILT_API_KEY"]
IMPORTANT: Add .env to your .gitignore to prevent committing secrets.
For production deployments, embed it in environment variables or AWS secrets manager.
import os
import quilt3
# Load from environment
api_key = os.environ["QUILT_API_KEY"]
registry_url = os.environ["QUILT_REGISTRY_URL"]
quilt3.login_with_api_key(api_key, registry_url=registry_url)
# Now use Quilt normally
pkg = quilt3.Package.browse("mypackage", "s3://mybucket")
NOTE: quilt3 will not automatically detect and use the QUILT_API_KEY. You must explicitly login with it.
import quilt3
# List all your keys
keys = quilt3.api_keys.list()
for key in keys:
print(f"\n{key.name}")
print(f" Owner: {key.user_email}")
print(f" ID: {key.id}")
print(f" Fingerprint: {key.fingerprint}")
print(f" Status: {key.status}")
print(f" Created: {key.created_at}")
print(f" Expires: {key.expires_at}")
print(f" Last used: {key.last_used_at or 'Never'}")
# Find keys by name
production_keys = quilt3.api_keys.list(name="production-pipeline")
# Find keys by fingerprint
key = quilt3.api_keys.list(fingerprint="qk_abc...xyz")
# Find only active keys
active_keys = quilt3.api_keys.list(status="ACTIVE")
# Find expired keys
expired_keys = quilt3.api_keys.list(status="EXPIRED")
# Get details about a specific key
key = quilt3.api_keys.get("key-id-here")
if key:
print(f"Name: {key.name}")
print(f"Owner: {key.user_email}")
print(f"Status: {key.status}")
if key.status == "EXPIRED":
print("This key has expired and needs to be rotated")
else:
print("Key not found")
# Revoke by ID (if you know it)
quilt3.api_keys.revoke(id="key-id-here")
# Or revoke by secret (useful for immediate emergency revocation)
quilt3.api_keys.revoke(secret="qk_your_secret")
.env files to .gitignoreci-github-prod-2026q1Administrators have additional capabilities to manage API keys across all users.
You must be an admin user to access these functions:
import quilt3.admin
# List all keys in the system
all_keys = quilt3.admin.api_keys.list()
print(f"Total keys: {len(all_keys)}")
# Filter by user email
user_keys = quilt3.admin.api_keys.list(email="user@example.com")
print(f"Keys for user@example.com: {len(user_keys)}")
# Filter by key name
pipeline_keys = quilt3.admin.api_keys.list(key_name="ci-pipeline")
# Filter by status
active_keys = quilt3.admin.api_keys.list(status="ACTIVE")
expired_keys = quilt3.admin.api_keys.list(status="EXPIRED")
print(f"Active keys: {len(active_keys)}")
print(f"Expired keys: {len(expired_keys)}")
# Get details about a specific key
key = quilt3.admin.api_keys.get("key-id-here")
if key:
print(f"Key: {key.name}")
print(f"Owner: {key.user_email}")
print(f"ID: {key.id}")
print(f"Status: {key.status}")
print(f"Created: {key.created_at}")
print(f"Expires: {key.expires_at}")
print(f"Last used: {key.last_used_at or 'Never'}")
# Revoke a specific key by ID
key_id = "key-id-here"
quilt3.admin.api_keys.revoke(id=key_id)
print(f"Revoked key: {key_id}")
from datetime import datetime, timedelta
# Find keys that haven't been used in 90 days
threshold = datetime.now() - timedelta(days=90)
all_keys = quilt3.admin.api_keys.list(status="ACTIVE")
unused_keys = []
for key in all_keys:
if key.last_used_at is None or key.last_used_at < threshold:
unused_keys.append(key)
print(f"⚠️ Unused key: {key.name}")
print(f" Owner: {key.user_email}")
print(f" ID: {key.id}")
print(f" Last used: {key.last_used_at or 'Never'}")
print(f"\nFound {len(unused_keys)} unused keys")
# Get summary statistics
all_keys = quilt3.admin.api_keys.list()
active = sum(1 for k in all_keys if k.status == "ACTIVE")
expired = sum(1 for k in all_keys if k.status == "EXPIRED")
print("API Key Usage Report")
print("=" * 60)
print(f"Total keys: {len(all_keys)}")
print(f"Active: {active}")
print(f"Expired: {expired}")
# Show recent keys
print("\nRecently created keys:")
sorted_keys = sorted(all_keys, key=lambda k: k.created_at, reverse=True)
for key in sorted_keys[:10]:
print(f" {key.name}")
print(f" Created: {key.created_at}")
print(f" Status: {key.status}")
print(f" Owner: {key.user_email}")
For detailed audit trail queries, see the GxP documentation.
Example query to find all API key usage in the last 30 days:
SELECT
eventtime,
eventname,
json_extract_scalar(useridentity, '$.email') as user_email,
json_extract_scalar(useridentity, '$.sessionContext.auth.keyName') as key_name,
json_extract_scalar(useridentity, '$.sessionContext.auth.keyFingerprint') as key_fingerprint,
sourceipaddress
FROM audit_trail
WHERE date >= date_format(current_date - interval '30' day, '%Y/%m/%d')
AND json_extract_scalar(useridentity, '$.sessionContext.auth.type') = 'api_key'
ORDER BY eventtime DESC
LIMIT 100;
Error: Authentication failed. Check your credentials or API key.
api_key = "qk_..." # Must start with qk_
if not api_key.startswith("qk_"):
print("Invalid key format!")
import quilt3
quilt3.login() # Use interactive login first
keys = quilt3.api_keys.list()
for key in keys:
print(f"{key.name}: {key.status}")
import os
import quilt3
api_key = os.environ["QUILT_API_KEY"]
registry_url = os.environ["QUILT_REGISTRY_URL"]
quilt3.logout() # Clear all credentials
quilt3.login_with_api_key(api_key, registry_url=registry_url) # Try again
Error: API key must start with 'qk_' prefix
api_key = api_key.strip()Error: Key shows status: EXPIRED
Create a new key:
import quilt3
# Check expiration
quilt3.login()
keys = quilt3.api_keys.list(name="my-key")
for key in keys:
print(f"Status: {key.status}")
print(f"Expires: {key.expires_at}")
# Create new key
new_key, secret = quilt3.api_keys.create("my-key-v2", expires_in_days=90)
print(f"New secret: {secret}")
# Revoke old key
quilt3.api_keys.revoke(id=keys[0].id)
Error: QUILT_API_KEY environment variable not set
import os
print(os.environ.get("QUILT_API_KEY")) # Should show qk_...
export QUILT_API_KEY="qk_your_secret"
For ~/.bashrc or ~/.zshrc:
echo 'export QUILT_API_KEY="qk_..."' >> ~/.bashrc
source ~/.bashrc
echo 'QUILT_API_KEY="qk_..."' >> .env
Then load it in Python:
from dotenv import load_dotenv
load_dotenv()
If you have existing scripts using quilt3.login(), here’s how to migrate:
import quilt3
# Requires browser - doesn't work in CI/CD
quilt3.login()
pkg = quilt3.Package.browse("data/latest", "s3://mybucket")
import os
import quilt3
# One-time setup: Create API key
# (Run this once on your local machine)
# quilt3.login()
# key, secret = quilt3.api_keys.create("my-script")
# print(f"Secret: {secret}")
# In your script: Use the API key
api_key = os.environ.get("QUILT_API_KEY")
if not api_key:
raise ValueError("QUILT_API_KEY environment variable required")
quilt3.login_with_api_key(
api_key,
registry_url=os.environ["QUILT_REGISTRY_URL"],
)
pkg = quilt3.Package.browse("data/latest", "s3://mybucket")
login_with_api_key()For detailed API documentation, see: