Skip to content

Twilio Credential Security: API Key Management and Best Practices

DodaTech Updated 2026-06-28 7 min read

In this tutorial, you will learn about Twilio Credential Security: API Key Management and Best Practices. We cover key concepts, practical examples, and best practices to help you master this topic.

Twilio credential security involves managing API keys and Auth Tokens, creating restricted keys per environment, isolating traffic with subaccounts, rotating credentials regularly, and monitoring for unauthorized API access.

What You'll Learn

How to create and manage Twilio API keys, use restricted API keys with granular permissions, isolate environments with subaccounts, rotate credentials on a schedule, configure IP access control, detect unauthorized use, and follow security best practices.

Why It Matters

A leaked Auth Token gives attackers full control of your Twilio account — sending SMS on your dime, modifying Webhooks, and accessing logs. DodaTech follows strict credential management with per-service keys and automated rotation.

Real-World Use

A developer accidentally commits an API key to GitHub. DodaTech's credential scanner detects the exposure within 5 minutes, automatically rotates the compromised key, revokes access, and alerts the security team — preventing any unauthorized usage.

flowchart LR
    A["Create API\nKey"] --> B{"Key Type"}
    B -->|Main| C["Full Access\nAuth Token"]
    B -->|Restricted| D["Scoped\nAPI Key"]
    D --> E["Service A\nSMS Only"]
    D --> F["Service B\nVerify Only"]
    C --> G["Subaccount\nIsolation"]
    G --> H["Dev\nSubaccount"]
    G --> I["Prod\nSubaccount"]
    style A fill:#f22f46,color:#fff
    style D fill:#dbeafe,stroke:#2563eb
    style G fill:#bbf7d0,stroke:#16a34a

Creating API Keys

import os
from twilio.rest import Client

client = Client(
    os.environ["TWILIO_ACCOUNT_SID"],
    os.environ["TWILIO_AUTH_TOKEN"]
)

# Create a standard API key
def create_api_key(name, key_type="standard"):
    """Create a new API key.
    Types: standard (full access), restricted (scoped)
    """
    key = client.new_keys.create(
        friendly_name=name,
        key_type=key_type
    )
    print(f"Key SID: {key.sid}")
    print(f"Name: {key.friendly_name}")
    print(f"Type: {key.key_type}")
    print(f"Secret: {key.secret[:10]}...")  # Only shown once!
    print("SAVE THIS SECRET - it will not be shown again.")
    return key

# key = create_api_key("DodaTech SMS Service", "standard")
# Expected output:
# Key SID: SKxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Name: DodaTech SMS Service
# Type: standard
# Secret: GSb3c4d5e...
# SAVE THIS SECRET - it will not be shown again.

Restricted API Keys

# Create restricted API key with granular permissions
def create_restricted_key(name, permissions, subaccount_sid=None):
    """Create an API key restricted to specific services.

    permissions: list of allowed services
    Example: ["sms:send", "verify:create", "calls:make"]
    """
    params = {
        "friendly_name": name,
        "key_type": "restricted",
        "restricted_data_transfer": {
            "direction": "inbound,outbound"
        }
    }
    if subaccount_sid:
        params["subaccount_sid"] = subaccount_sid

    key = client.new_keys.create(**params)
    print(f"Restricted Key: {key.sid}")
    print(f"Name: {key.friendly_name}")
    print(f"Permissions: {permissions}")
    return key

# restricted_key = create_restricted_key(
#     "Verify Service Only",
#     ["verify:create", "verify:check"]
# )
# Expected output:
# Restricted Key: SKyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
# Name: Verify Service Only
# Permissions: ['verify:create', 'verify:check']

# Using restricted key instead of Auth Token
restricted_client = Client(
    os.environ["TWILIO_ACCOUNT_SID"],
    "GS_restricted_key_secret_here"
)

# Test restricted access
try:
    result = restricted_client.verify.v2.services("VAxxx").verifications.create(
        to="+14155551234",
        channel="sms"
    )
    print(f"Verify API works: {result.sid}")
except Exception as e:
    print(f"Verify API error: {e}")

Subaccounts for Environment Isolation

# Create a subaccount for development
def create_subaccount(name):
    subaccount = client.api.accounts.create(
        friendly_name=name
    )
    print(f"Subaccount SID: {subaccount.sid}")
    print(f"Name: {subaccount.friendly_name}")
    print(f"Status: {subaccount.status}")
    return subaccount

# dev_acct = create_subaccount("DodaTech Dev")
# Expected output:
# Subaccount SID: ACyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
# Name: DodaTech Dev
# Status: active

# Create API key for subaccount
def create_subaccount_key(subaccount_sid, name):
    """Create a key that operates only within the given subaccount."""
    subaccount_client = Client(
        subaccount_sid,
        os.environ["TWILIO_AUTH_TOKEN"]
    )
    key = subaccount_client.new_keys.create(
        friendly_name=name
    )
    print(f"Subaccount Key: {key.sid}")
    print(f"Scope: {subaccount_sid}")
    return key

# List all subaccounts
def list_subaccounts():
    accounts = client.api.accounts.list()
    print("All accounts:")
    for acct in accounts:
        print(f"  {acct.friendly_name}: {acct.sid[:20]}... ({acct.status})")
    return accounts

# list_subaccounts()
# Expected output:
# All accounts:
#   DodaTech Main: ACxxxxxxxxxxxxxxxx... (active)
#   DodaTech Dev: ACyyyyyyyyyyyyyyyy... (active)

Credential Rotation

import schedule
import time

def rotate_credentials():
    """Rotate API keys: create new, switch apps, delete old."""
    print(f"[{time.ctime()}] Starting credential rotation...")

    # 1. Create new key
    new_key = client.new_keys.create(
        friendly_name=f"DodaTech SMS {time.strftime('%Y-%m')}"
    )
    print(f"  Created: {new_key.sid}")

    # 2. In production, deploy new key to environment
    # (This is handled by your CI/CD system)
    print(f"  Deploy new key secret to production")

    # 3. After confirming new key works, delete old key
    old_key_sid = "SK_old_key_to_delete"
    try:
        client.new_keys(old_key_sid).delete()
        print(f"  Deleted old key: {old_key_sid}")
    except Exception as e:
        print(f"  Could not delete old key: {e}")

    print("  Rotation complete.")
    return new_key

# Schedule monthly rotation
# schedule.every().month.do(rotate_credentials)

# rotate_credentials()
# Expected output:
# [Mon Jun 28 10:00:00 2026] Starting credential rotation...
#   Created: SKxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
#   Deploy new key secret to production
#   Deleted old key: SK_old_key_to_delete
#   Rotation complete.

Monitoring for Unauthorized Use

def check_unauthorized_activity(hours_back=24):
    """Check for suspicious API activity."""
    from datetime import datetime, timedelta
    start_time = datetime.utcnow() - timedelta(hours=hours_back)

    # Check for failed auth attempts
    events = client.monitor.events.list(
        start_date=start_time,
        level="ERROR",
        limit=50
    )

    suspicious = []
    for event in events:
        if event.description and "authentication" in event.description.lower():
            suspicious.append(event)
            print(f"SUSPICIOUS: {event.date_created}")
            print(f"  {event.description}")
            print(f"  SID: {event.sid}")
            print()

    print(f"Found {len(suspicious)} suspicious events in {hours_back}h")
    return suspicious

# check_unauthorized_activity()
# Expected output:
# Found 0 suspicious events in 24h

# Check API key usage
def list_api_keys_and_usage():
    keys = client.new_keys.list()
    print(f"Active API keys: {len(keys)}")
    for k in keys:
        print(f"  {k.friendly_name}: {k.sid[:20]}... ({k.date_created})")
        print(f"    Type: {k.key_type}")
    return keys

Common Mistakes

1. Using the Main Auth Token in Every Service

Your main Auth Token has full account access. Use restricted API keys per service. If a key leaks, only that service is compromised, not your entire account.

2. Hardcoding Credentials in Source Code

Credentials in source code end up in version control. Use environment variables in development, secrets manager (AWS Secrets Manager, HashiCorp Vault) in production.

3. Never Rotating Keys

Without rotation, a leaked key works indefinitely. Rotate API keys every 90 days minimum. Use automation to rotate without downtime.

4. Sharing Auth Tokens Across Environments

Use separate subaccounts for dev, staging, and production. Each has its own keys and billing. A dev mistake won't affect production data or costs.

5. Not Monitoring for Anomalies

You won't know about leaked credentials until the bill arrives. Monitor for unusual sending patterns, new key creation, and failed auth attempts. Set up alerts.

Practice Questions

  1. What is the difference between an Auth Token and an API Key?
  2. What are restricted API keys and why use them?
  3. What is a subaccount and how does it improve security?
  4. How often should you rotate credentials?

Answers:

  1. Auth Token (starts with no prefix, or you get it from Console) has full account access. API Keys (prefix SK) can be restricted per service. API Keys are safer for individual service use.
  2. Restricted API Keys limit access to specific services (SMS only, Verify only). If the key leaks, attackers can only use that specific service, minimizing Blast Radius.
  3. A subaccount is an isolated Twilio account under your main account. Use separate subaccounts for dev, staging, and production. Each has independent keys, usage, and billing.
  4. Every 90 days minimum. Automate rotation with a scheduled job that creates a new key, deploys it, and deletes the old one. Rotate immediately if you suspect a leak.

Challenge: Build a credential management system: create restricted API keys for 3 services (SMS, Verify, Voice), set up subaccounts for dev and production, implement a credential rotation script with zero-downtime key switching, configure IP access control for production keys, set up alerting for new key creation and failed auth events, and write a credential leak Incident Response procedure.

FAQ

What happens if my Auth Token is leaked?

Anyone with your Auth Token can send SMS, make calls, view logs, and modify webhooks on your account. Rotate immediately, revoke all existing keys, and audit logs for unauthorized usage.

Can I delete an API key?

Yes, call client.new_keys(sid).delete(). The key is immediately invalidated. API calls using that key will fail with error 20003 (authentication failure).

How do I restrict API access to specific IP addresses?

In Console > Account > Settings > API Credentials > IP Access Control. Add whitelisted IPs. Requests from other IPs are rejected. Use this for production environment keys.

What is the difference between standard and restricted key types?

Standard keys have the same access as your Auth Token. Restricted keys can be scoped to specific services (e.g., SMS only, Verify only). Restricted keys are recommended for service-specific use.

Can I use environment variables for Twilio credentials?

Yes, this is the recommended approach. Set TWILIO_ACCOUNT_SID and TWILIO_API_KEY/TWILIO_AUTH_TOKEN as environment variables. Never commit them to version control.

Mini Project

Build a credential lifecycle management system: create 3 restricted API keys for different services, set up subaccounts for dev/prod isolation, implement a credential rotation script with automated deployment, configure IP access control, create a monitoring dashboard for API key usage and anomalies, and write a security incident response plan.

What's Next

Complete Twilio Project — build a full notification system combining everything you learned.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro