> For the complete documentation index, see [llms.txt](https://docs.pullbay.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.pullbay.com/documentation/platform-best-practices/api-key-security.md).

# Api Key Security

Your API key is the primary credential for accessing Pullbay's data API. Protecting it is essential to prevent unauthorized access to your account, protect your credit balance, and ensure the integrity of your data. This guide covers best practices, emergency procedures, and infrastructure recommendations for securing your Pullbay API keys.

## The Importance of API Key Security

Your Pullbay API key grants full access to your account and all associated data. If compromised, an attacker could:

* Deplete your credit balance by making unnecessary API requests
* Access all data your keys are authorized to retrieve
* Impersonate your application or service
* Potentially violate data regulations if the attacker misuses the data
* Damage your reputation and that of your organization

Proper API key management is not optional—it's a fundamental part of operating a secure data pipeline.

## API Key Security Fundamentals

### Do's — Security Best Practices

| Practice                                                           | Benefit                                                  |
| ------------------------------------------------------------------ | -------------------------------------------------------- |
| Store keys in environment variables or secret managers             | Separates credentials from code                          |
| Use different keys for different environments (prod, staging, dev) | Limits blast radius if one key is compromised            |
| Use different keys for different applications or services          | Isolates access and simplifies revocation                |
| Use descriptive, systematic key names                              | Makes it easy to identify which key serves which purpose |
| Rotate keys periodically (every 90 days)                           | Reduces the window of exposure if an old key is leaked   |
| Revoke keys immediately if exposed                                 | Prevents continued misuse                                |
| Use the minimum number of keys you need                            | Reduces attack surface and management overhead           |
| Monitor key activity regularly                                     | Detects suspicious usage patterns early                  |
| Implement least-privilege access principles                        | Only grant keys the minimum permissions required         |
| Document your key management process                               | Ensures consistency across your team                     |

### Don'ts — Dangerous Practices to Avoid

| Anti-Pattern                                                   | Why It's Dangerous                                               |
| -------------------------------------------------------------- | ---------------------------------------------------------------- |
| Commit API keys to version control (Git, GitHub, etc.)         | Once in Git history, the key is exposed forever                  |
| Include API keys in client-side code (JavaScript, mobile apps) | Anyone can inspect your app and steal the key                    |
| Share API keys via email, Slack, or chat applications          | These channels are not secure and create audit trail problems    |
| Use the same API key across all projects and environments      | A compromise in one project exposes all of them                  |
| Keep unused API keys active                                    | Increases your attack surface and management burden              |
| Log or display API keys in error messages                      | Logs and error messages may be accessible to unauthorized people |
| Hard-code API keys in configuration files                      | Makes key rotation difficult and increases exposure risk         |
| Share keys between team members via insecure methods           | Creates a breakdown in access control and auditability           |

## If Your API Key Is Exposed

**Immediate Actions (within minutes):**

{% stepper %}
{% step %}

#### Revoke the Compromised Key Immediately

* Log into your Pullbay account
* Navigate to API Keys management
* Click "Revoke" on the exposed key
* This stops any new requests using that key
  {% endstep %}

{% step %}

#### Create a New API Key

* In the API Keys section, click "Generate New Key"
* Use a descriptive name (e.g., `prod-api-server-rotated-2026-04`)
* Note the new key securely
  {% endstep %}

{% step %}

#### Update Your Application

* Deploy the new API key to all systems using the old one
* Update environment variables, secret managers, and configuration
* Verify all services are using the new key before proceeding
  {% endstep %}

{% step %}

#### Verify the Update

* Test each application/service to confirm it's working with the new key
* Check the API dashboard to confirm requests are coming from the new key
* Monitor error rates to catch any services that are still using the old key
  {% endstep %}

{% step %}

#### Review Usage and Investigate

* Check your usage dashboard for suspicious activity during the exposure window
* Look for unusual request patterns, unexpected endpoints, or geographic anomalies
* If you find malicious activity, investigate the source
* Consider implementing additional monitoring and alerts
  {% endstep %}
  {% endstepper %}

**Follow-up Actions (within 24 hours):**

* Check your credit balance for unexpected charges
* Review recent API request logs for any unauthorized data access
* Communicate with your team about the incident
* Update your incident response and key management procedures
* Consider rotating other keys as a precaution
* If the key was exposed in a public repository, notify GitHub and other platforms so they can scan and remove it

**Long-term Actions (ongoing):**

* Implement automated secret scanning in your repositories
* Set up alerts for unusual API activity
* Establish a regular key rotation schedule
* Document the incident and lessons learned

## API Key Management Features

### Multiple API Keys

Pullbay allows you to create and manage multiple API keys within a single account. This is one of your most important security tools.

**Use Cases for Multiple Keys:**

| Key Name Example        | Purpose                     | Environment | Usage                     |
| ----------------------- | --------------------------- | ----------- | ------------------------- |
| `prod-api-server`       | Production application      | Production  | Main data pipeline        |
| `prod-data-warehouse`   | Data warehouse integration  | Production  | Scheduled ETL jobs        |
| `staging-data-pipeline` | Staging application testing | Staging     | Testing new features      |
| `dev-local-testing`     | Developer laptop testing    | Development | Local development         |
| `ci-cd-tests`           | Automated test suite        | CI/CD       | GitHub Actions, GitLab CI |
| `partner-integration`   | Third-party partner access  | Production  | Partner API access        |

**Benefits of Multiple Keys:**

* **Isolation:** If one key is compromised, only the associated service is affected
* **Accountability:** You can track which key is used for which requests
* **Auditability:** Different keys can have different activity logs
* **Easy Revocation:** Revoke a specific key without disrupting other services
* **Least Privilege:** Each key can be associated with specific monitoring and restrictions
* **Team Organization:** Different team members can manage different keys

**How to Create Multiple Keys:**

1. Log into your Pullbay account
2. Go to "Settings" → "API Keys"
3. Click "Generate New API Key"
4. Enter a descriptive name following your naming convention
5. Review the key details and copy it immediately
6. Store it securely in your secret manager or environment variable system
7. Test the key by making a test API request
8. Document the key's purpose and which service uses it

### Key Rotation Without Downtime

The ability to rotate keys without downtime is essential for production systems. Pullbay supports multiple active keys simultaneously, allowing you to rotate keys safely.

**The Zero-Downtime Rotation Process:**

```
Step 1: Generate New Key
  └─ New key is immediately active
  └─ Old key continues working during transition

Step 2: Update All Services
  └─ Deploy new key to production code/config
  └─ Services gradually transition to new key
  └─ Monitor that transition is complete

Step 3: Verify New Key Usage
  └─ Monitor API dashboard for requests using new key
  └─ Confirm all services are using the new key
  └─ Wait a few hours to ensure all instances have updated

Step 4: Revoke Old Key
  └─ Once confident all services use new key
  └─ Revoke the old key to prevent accidental usage
  └─ Rotation is complete

Rollback: If something goes wrong, the old key still works until revoked
```

**Step-by-Step Rotation Guide:**

{% stepper %}
{% step %}

#### Preparation

**Duration:** 30 minutes

1. Schedule a maintenance window (optional but recommended)
2. Notify your team that a key rotation is happening
3. Generate a new API key in your Pullbay account
4. Copy the new key and store it temporarily in a secure location
5. Prepare your deployment process for the new key
   {% endstep %}

{% step %}

#### Deployment

**Duration:** 30-60 minutes

1. Update your environment variables or secret manager with the new key
2. Deploy the updated configuration to all your services
3. For containerized deployments, restart containers with new config
4. For serverless, redeploy functions
5. For scheduled jobs, ensure the next scheduled run uses the new key
   {% endstep %}

{% step %}

#### Verification

**Duration:** 15-30 minutes

1. Make test requests to verify your services work with the new key
2. Monitor your API dashboard for new key requests
3. Check error rates and latencies—they should remain normal
4. Confirm all expected services are using the new key
5. Look for any failed requests or connection errors
   {% endstep %}

{% step %}

#### Monitoring

**Duration:** 30 minutes to several hours

1. Monitor your production metrics for the next 1-2 hours
2. Watch for any unexpected behavior or error spikes
3. If any service fails, you can temporarily revert to the old key
4. Once confident everything is working, proceed to cleanup
   {% endstep %}

{% step %}

#### Cleanup

**Duration:** Immediate

1. Review logs to confirm the old key is no longer being used
2. Revoke the old key in your Pullbay account
3. Archive the old key in your records
4. Update your key rotation documentation
5. Notify your team that rotation is complete
   {% endstep %}
   {% endstepper %}

**Example Rotation Timeline:**

```
08:00 AM - Schedule maintenance window and notify team
08:15 AM - Generate new API key in Pullbay account
08:20 AM - Update environment variables in secret manager
08:25 AM - Deploy new configuration to production
08:45 AM - Verify all services are using new key
09:00 AM - Confirm no errors or issues
09:05 AM - Revoke old API key
09:10 AM - Document completion and notify team
```

**Important Notes:**

* Pullbay supports multiple active keys, so there's no cutoff moment—both keys work during transition
* You can take your time verifying everything works before revoking the old key
* If something goes wrong during deployment, the old key still works as a fallback
* Always keep the old key active until you're 100% certain the new key is in use everywhere

### Key Naming Conventions

A systematic naming convention makes it easy to identify which key serves which purpose and simplifies management.

**Recommended Naming Pattern:**

```
{environment}-{service/app}-{description}
```

**Example Keys:**

| Key Name               | Environment | Service        | Description      | Usage                     |
| ---------------------- | ----------- | -------------- | ---------------- | ------------------------- |
| `prod-api-server`      | Production  | API Server     | Main application | Real-time data requests   |
| `prod-data-warehouse`  | Production  | Data Warehouse | ETL job          | Scheduled data collection |
| `staging-web-app`      | Staging     | Web App        | Testing          | Feature testing           |
| `staging-notebooks`    | Staging     | Notebooks      | Analysis         | Data science analysis     |
| `dev-local-testing`    | Development | Local Dev      | Personal         | Developer laptop          |
| `ci-cd-github-actions` | CI/CD       | GitHub         | Automated tests  | Test suite runner         |
| `ci-cd-gitlab-ci`      | CI/CD       | GitLab         | Automated tests  | Alternative CI/CD         |
| `partner-acme-corp`    | Production  | Partner        | Third-party      | Partner integration       |
| `archive-2025-retired` | Archive     | Archive        | Rotated out      | Old key (to be deleted)   |

**Benefits of This Convention:**

* **At a glance:** You know the environment, service, and purpose
* **Searchability:** Easy to filter keys in your dashboard
* **Documentation:** The name serves as self-documentation
* **Security:** Clear ownership and responsibility for each key
* **Automation:** Scripts can parse key names to determine handling

**Naming Rules:**

* Use lowercase letters, numbers, and hyphens only
* Avoid special characters that may cause parsing issues
* Keep names under 50 characters for readability
* Include environment as first component for clarity
* Include service/app as second component
* Add descriptive suffix if needed for clarity
* Don't include the actual key value in the name (obviously)
* Don't include personally identifiable information in key names

## Activity Logging and Monitoring

Pullbay logs all API requests made with your keys. This activity log is crucial for security monitoring and troubleshooting.

### What Pullbay Logs

For each API request, Pullbay records:

| Field                | Details                                           |
| -------------------- | ------------------------------------------------- |
| **Timestamp**        | When the request was made (UTC)                   |
| **API Key**          | Which key was used (last 8 characters visible)    |
| **Endpoint**         | Which data endpoint was requested                 |
| **Request Status**   | Success, rate limit, authentication error, etc.   |
| **Results Returned** | Number of results in the response                 |
| **Credits Consumed** | How many credits the request cost                 |
| **User Agent**       | Your application's HTTP user agent string         |
| **Request ID**       | Unique identifier for the request (for debugging) |
| **Response Time**    | How long the request took to complete             |

### Accessing Activity Logs

**In the Pullbay Dashboard:**

1. Log into your Pullbay account
2. Go to "Monitoring" → "Activity Log"
3. Filter by:
   * Date range (today, last 7 days, last 30 days, custom)
   * Specific API key
   * Endpoint
   * Status (success, error, rate limited)
4. Export activity as CSV for analysis
5. Set up alerts for unusual patterns

**Programmatic Access:**

Coming soon—API endpoint for querying activity logs programmatically.

### Detecting Suspicious Activity

Watch your activity logs for these red flags:

| Suspicious Pattern                                 | Possible Cause                                | Action                                     |
| -------------------------------------------------- | --------------------------------------------- | ------------------------------------------ |
| Sudden spike in request volume                     | Compromised key or runaway code               | Check the key and application immediately  |
| Requests from unexpected geographic locations      | Key leaked to unauthorized party              | Revoke key if not explained                |
| Failed authentication errors in high volume        | Attacker trying random keys                   | Monitor for additional signs of compromise |
| Requests to endpoints your app doesn't use         | Attacker exploring your data                  | Investigate what data was accessed         |
| Usage at unusual times (middle of night, weekends) | Unauthorized access                           | Compare to your known usage patterns       |
| Requests from unexpected user agents               | Unauthorized application or script            | Identify the user agent and take action    |
| Activity on a "disabled" or "rotated" key          | Key not properly revoked or stored insecurely | Revoke the key immediately                 |

**Example:** If your application only requests app store reviews but you see requests to a different endpoint, that's a sign your key may be compromised.

### Setting Up Monitoring Alerts

In your Pullbay account, configure alerts for:

* **Credit threshold alerts:** Notify you when credits fall below a certain amount
* **Unusual activity alerts:** Sudden spike in request volume above your baseline
* **Key compromise alerts:** Failed authentication attempts, requests outside expected hours
* **Rate limit alerts:** When approaching or hitting rate limits
* **Error rate alerts:** Spike in failed requests

Configure these in Settings → Alerts → API Key Monitoring.

## Infrastructure Recommendations by Deployment Level

Secure API key management depends on your deployment context. Here are recommendations for different scenarios.

### Production Environment — Secret Manager (Recommended)

For production systems, use a dedicated secret management service:

**AWS Secrets Manager:**

```python
import boto3
import json

client = boto3.client('secretsmanager', region_name='us-east-1')

try:
    secret = client.get_secret_value(SecretId='pullbay-api-key-prod')
    api_key = json.loads(secret['SecretString'])['api_key']
except Exception as e:
    print(f"Error retrieving secret: {e}")
    raise

# Use api_key for Pullbay requests
```

**Google Secret Manager:**

```python
from google.cloud import secretmanager

def access_secret_version(secret_id, version_id="latest"):
    client = secretmanager.SecretManagerServiceClient()
    name = f"projects/{project_id}/secrets/{secret_id}/versions/{version_id}"
    response = client.access_secret_version(request={"name": name})
    return response.payload.data.decode("UTF-8")

api_key = access_secret_version("pullbay-api-key-prod")
```

**HashiCorp Vault:**

```python
import hvac

client = hvac.Client(url='https://vault.example.com', token='s.xxxxxxxx')
secret = client.secrets.kv.read_secret_version(path='pullbay/prod')
api_key = secret['data']['data']['api_key']
```

**Azure Key Vault:**

```python
from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient

credential = DefaultAzureCredential()
client = SecretClient(vault_url="https://my-vault.vault.azure.net/", credential=credential)
api_key = client.get_secret("pullbay-api-key-prod").value
```

**Benefits:**

* Centralized key management and rotation
* Detailed audit logs of all key access
* Encryption at rest and in transit
* Fine-grained access control (who can access which secrets)
* Automatic key rotation support
* Integration with your infrastructure as code
* Audit logging and access control policies

**When to Use:**

* All production deployments
* Any environment with sensitive data
* Enterprise deployments requiring audit trails
* Regulated industries (finance, healthcare, etc.)

### Simple Setups — Environment Variables

For development and simple deployments, environment variables offer a good balance of simplicity and security.

**Store API Key as Environment Variable:**

```bash
# .env file (NEVER commit to version control)
PULLBAY_API_KEY=api_key_xxxxxxxxxxxxxxxx

# Or set directly in shell
export PULLBAY_API_KEY="api_key_xxxxxxxxxxxxxxxx"
```

**Load in Python:**

```python
import os
from dotenv import load_dotenv  # pip install python-dotenv

# Load from .env file in development
load_dotenv()

api_key = os.getenv('PULLBAY_API_KEY')
if not api_key:
    raise ValueError("PULLBAY_API_KEY environment variable not set")

# Use api_key for Pullbay requests
```

**Load in Node.js:**

```javascript
require('dotenv').config();

const apiKey = process.env.PULLBAY_API_KEY;
if (!apiKey) {
    throw new Error('PULLBAY_API_KEY environment variable not set');
}

// Use apiKey for Pullbay requests
```

**Load in PHP:**

```php
<?php
require 'vendor/autoload.php';
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

$apiKey = $_ENV['PULLBAY_API_KEY'];
if (!$apiKey) {
    throw new Exception('PULLBAY_API_KEY environment variable not set');
}

// Use $apiKey for Pullbay requests
```

**Key Rules for Environment Variables:**

1. **NEVER commit .env files to version control**

   ```bash
   # Add to .gitignore
   .env
   .env.local
   .env.*.local
   ```
2. **Create .env.example with placeholders**

   ```bash
   # .env.example (SAFE to commit)
   PULLBAY_API_KEY=your_api_key_here
   PULLBAY_BASE_URL=https://api.pullbay.io
   ```
3. **Document environment variables in your README**

   ````markdown
   ## Configuration

   Create a .env file based on .env.example:
   ```bash
   cp .env.example .env
   ````

   Then add your Pullbay API key:

   ```bash
   PULLBAY_API_KEY=api_key_xxxxxxxxxxxxxxxx
   ```
4. **Load early in your application startup**
   * Load environment variables before using them
   * Fail fast if required variables are missing
   * Validate that the API key format is correct
5. **Use environment-specific variables**

   ```bash
   # Development
   PULLBAY_API_KEY_DEV=dev_key_xxxxxxx

   # Staging
   PULLBAY_API_KEY_STAGING=staging_key_xxxxxxx

   # Production
   PULLBAY_API_KEY_PROD=prod_key_xxxxxxx
   ```

**When to Use Environment Variables:**

* Development and testing
* Simple applications with few dependencies
* Containerized deployments (Docker, Kubernetes)
* Simple cloud deployments (Heroku, AWS Lambda)
* Any scenario where a secret manager is not available

### CI/CD Pipelines — Platform Secret Storage

CI/CD platforms (GitHub Actions, GitLab CI, Jenkins, etc.) have built-in secret management.

**GitHub Actions:**

```yaml
# .github/workflows/pullbay-job.yml
name: Pullbay Data Pipeline

on:
  schedule:
    - cron: '0 2 * * *'  # Daily at 2 AM UTC

jobs:
  fetch-data:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'

      - name: Install dependencies
        run: pip install -r requirements.txt

      - name: Fetch data from Pullbay
        env:
          PULLBAY_API_KEY: ${{ secrets.PULLBAY_API_KEY_PROD }}
        run: python scripts/fetch_reviews.py
```

**To Add the Secret to GitHub:**

1. Go to your GitHub repository
2. Settings → Secrets and variables → Actions
3. Click "New repository secret"
4. Name: `PULLBAY_API_KEY_PROD`
5. Value: (paste your API key)
6. Click "Add secret"

**GitLab CI:**

```yaml
# .gitlab-ci.yml
fetch_data:
  stage: scheduled
  script:
    - pip install -r requirements.txt
    - python scripts/fetch_reviews.py
  variables:
    PULLBAY_API_KEY: $PULLBAY_API_KEY_PROD
  only:
    - schedules
```

**To Add the Secret to GitLab:**

1. Go to your GitLab project
2. Settings → CI/CD → Variables
3. Click "Add variable"
4. Key: `PULLBAY_API_KEY_PROD`
5. Value: (paste your API key)
6. Check "Protect variable" to limit visibility
7. Click "Add variable"

**Jenkins:**

```groovy
pipeline {
    agent any

    stages {
        stage('Fetch Data') {
            steps {
                withCredentials([string(credentialsId: 'pullbay-api-key-prod', variable: 'PULLBAY_API_KEY')]) {
                    sh '''
                        pip install -r requirements.txt
                        python scripts/fetch_reviews.py
                    '''
                }
            }
        }
    }
}
```

**CI/CD Secret Management Best Practices:**

| Practice                                      | Benefit                                      |
| --------------------------------------------- | -------------------------------------------- |
| Use separate secrets per environment          | Limits exposure if one secret is compromised |
| Mark secrets as "protected" or "masked"       | Prevents accidental exposure in logs         |
| Limit secret access to specific jobs/branches | Reduces unauthorized access risk             |
| Rotate CI/CD secrets regularly                | Prevents long-term exposure                  |
| Use short-lived credentials if available      | Reduces the window of exposure               |
| Audit secret access logs                      | Detects unauthorized access attempts         |
| Don't echo secrets in logs                    | Prevents accidental exposure                 |
| Clean up artifacts that may contain secrets   | Prevents leakage through build artifacts     |

**Important:** Even though secrets are protected, never trust that they can't be exposed. Always assume CI/CD environments can be compromised and use the principle of least privilege.

## Code Examples — Secure Key Loading

Here are production-ready examples for loading your API key securely in common languages.

### Python

**With environment variables:**

```python
import os
from pullbay import PullbayClient

def get_api_key():
    """Load API key from environment variable safely."""
    api_key = os.getenv('PULLBAY_API_KEY')
    if not api_key:
        raise ValueError(
            "PULLBAY_API_KEY environment variable not set. "
            "Please set it before running this script."
        )

    # Basic validation
    if not api_key.startswith('api_key_'):
        raise ValueError(
            "Invalid API key format. API keys should start with 'api_key_'"
        )

    return api_key

def main():
    """Fetch data from Pullbay."""
    api_key = get_api_key()
    client = PullbayClient(api_key=api_key)

    # Make requests
    reviews = client.app_store_reviews(limit=10)
    print(f"Fetched {len(reviews)} reviews")

if __name__ == '__main__':
    main()
```

**With AWS Secrets Manager:**

```python
import boto3
import json
from pullbay import PullbayClient

def get_api_key_from_secrets_manager(secret_name='pullbay-api-key-prod'):
    """Load API key from AWS Secrets Manager."""
    client = boto3.client('secretsmanager', region_name='us-east-1')

    try:
        response = client.get_secret_value(SecretId=secret_name)
        secret = json.loads(response['SecretString'])
        return secret['api_key']
    except Exception as e:
        raise RuntimeError(f"Failed to retrieve API key from Secrets Manager: {e}")

def main():
    api_key = get_api_key_from_secrets_manager()
    client = PullbayClient(api_key=api_key)
    reviews = client.app_store_reviews(limit=10)
    print(f"Fetched {len(reviews)} reviews")

if __name__ == '__main__':
    main()
```

### Node.js

**With environment variables:**

```javascript
const axios = require('axios');

function getApiKey() {
    const apiKey = process.env.PULLBAY_API_KEY;
    if (!apiKey) {
        throw new Error(
            'PULLBAY_API_KEY environment variable not set. ' +
            'Please set it before running this script.'
        );
    }

    // Basic validation
    if (!apiKey.startsWith('api_key_')) {
        throw new Error(
            'Invalid API key format. API keys should start with "api_key_"'
        );
    }

    return apiKey;
}

async function fetchData() {
    const apiKey = getApiKey();

    const response = await axios.get('https://api.pullbay.io/app-store/reviews', {
        headers: {
            'Authorization': `Bearer ${apiKey}`,
            'User-Agent': 'my-app/1.0'
        },
        params: {
            limit: 10
        }
    });

    console.log(`Fetched ${response.data.results.length} reviews`);
}

fetchData().catch(error => {
    console.error('Error:', error.message);
    process.exit(1);
});
```

**With AWS Secrets Manager:**

```javascript
const AWS = require('aws-sdk');
const axios = require('axios');

const secretsManager = new AWS.SecretsManager({ region: 'us-east-1' });

async function getApiKeyFromSecretsManager(secretName = 'pullbay-api-key-prod') {
    try {
        const data = await secretsManager.getSecretValue({ SecretId: secretName }).promise();
        const secret = JSON.parse(data.SecretString);
        return secret.api_key;
    } catch (error) {
        throw new Error(`Failed to retrieve API key from Secrets Manager: ${error.message}`);
    }
}

async function fetchData() {
    const apiKey = await getApiKeyFromSecretsManager();

    const response = await axios.get('https://api.pullbay.io/app-store/reviews', {
        headers: {
            'Authorization': `Bearer ${apiKey}`,
            'User-Agent': 'my-app/1.0'
        },
        params: {
            limit: 10
        }
    });

    console.log(`Fetched ${response.data.results.length} reviews`);
}

fetchData().catch(error => {
    console.error('Error:', error.message);
    process.exit(1);
});
```

### PHP

**With environment variables:**

```php
<?php

class PullbayClient {
    private $apiKey;
    private $baseUrl = 'https://api.pullbay.io';

    public function __construct() {
        $this->apiKey = $this->getApiKey();
    }

    private function getApiKey() {
        $apiKey = $_ENV['PULLBAY_API_KEY'] ?? getenv('PULLBAY_API_KEY');

        if (!$apiKey) {
            throw new Exception(
                'PULLBAY_API_KEY environment variable not set. ' .
                'Please set it before running this script.'
            );
        }

        // Basic validation
        if (strpos($apiKey, 'api_key_') !== 0) {
            throw new Exception(
                'Invalid API key format. API keys should start with "api_key_"'
            );
        }

        return $apiKey;
    }

    public function fetchReviews($limit = 10) {
        $url = $this->baseUrl . '/app-store/reviews';

        $context = stream_context_create([
            'http' => [
                'method' => 'GET',
                'header' => [
                    'Authorization: Bearer ' . $this->apiKey,
                    'User-Agent: my-app/1.0'
                ]
            ]
        ]);

        $response = @file_get_contents($url . '?limit=' . $limit, false, $context);

        if ($response === false) {
            throw new Exception('Failed to fetch data from Pullbay API');
        }

        return json_decode($response, true);
    }
}

try {
    $client = new PullbayClient();
    $data = $client->fetchReviews(10);
    echo "Fetched " . count($data['results']) . " reviews\n";
} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
    exit(1);
}
```

**With Google Cloud Secret Manager:**

```php
<?php

use Google\Cloud\SecretManager\V1\SecretManagerServiceClient;

class PullbayClient {
    private $apiKey;
    private $baseUrl = 'https://api.pullbay.io';

    public function __construct($projectId) {
        $this->apiKey = $this->getApiKeyFromSecrets($projectId);
    }

    private function getApiKeyFromSecrets($projectId) {
        try {
            $client = new SecretManagerServiceClient();
            $name = $client->secretVersionName(
                $projectId,
                'pullbay-api-key-prod',
                'latest'
            );

            $response = $client->accessSecretVersion(['name' => $name]);
            $secret = json_decode($response->getPayload()->getData(), true);
            return $secret['api_key'];
        } catch (Exception $e) {
            throw new Exception(
                'Failed to retrieve API key from Secret Manager: ' . $e->getMessage()
            );
        }
    }

    public function fetchReviews($limit = 10) {
        // ... (same as previous example)
    }
}
```

## Frequently Asked Questions

<details>

<summary>How often should I rotate my API key?</summary>

**Recommendation:** Every 90 days, or immediately if exposed.

Industry best practices suggest rotating credentials every 90 days. However, you should rotate immediately if:

* The key has been exposed or compromised
* You've moved it to a new environment
* A team member with access to the key leaves your organization
* You suspect unauthorized access
* As part of a security incident response

Pullbay's zero-downtime rotation feature (create new → verify → revoke) makes frequent rotation painless.

</details>

<details>

<summary>What should I do if my key is leaked in a public repository?</summary>

**Immediate actions:**

1. **Revoke the key immediately** in your Pullbay account
2. **Contact GitHub/GitLab** to report the exposed secret
   * GitHub has a secret scanning feature that may already have detected it
   * You can manually report it via their security tools
3. **Create a new API key** and deploy it to all systems
4. **Force-push** to remove the secret from Git history (if possible):

   ```bash
   # Install git-filter-repo if needed
   pip install git-filter-repo

   # Remove the file from all history
   git filter-repo --path .env

   # Force push
   git push --force-with-lease
   ```
5. **Review your usage logs** for any unauthorized requests
6. **Monitor your account** for suspicious activity

**Prevention for the future:**

* Use a secret scanning tool in your CI/CD (e.g., GitGuardian, Snyk)
* Add a pre-commit hook to catch secrets before they're committed:

  ```bash
  pip install detect-secrets
  detect-secrets scan > .secrets.baseline
  ```
* Use `.gitignore` aggressively for sensitive files
* Educate your team about secret management

</details>

<details>

<summary>Can I restrict my API key to specific IP addresses?</summary>

**Current Status:** Not yet, but it's on our roadmap.

IP restriction would add an extra layer of security. In the meantime:

* Use separate keys for different environments and services
* Employ zero-trust network access (firewall rules at the infrastructure level)
* Use VPNs or private networks for accessing the Pullbay API
* Monitor your usage logs for requests from unexpected locations
* Consider API gateway solutions that can implement IP-based throttling

</details>

<details>

<summary>Can I see which team members have accessed which API keys?</summary>

**Current Status:** API key access logs are available in your Pullbay account.

You can view:

* When each key was created and last used
* Activity logs showing requests made by each key
* Failed authentication attempts

However, we don't currently track which team members accessed the secret manager or application code. This depends on your infrastructure:

* **AWS Secrets Manager:** CloudTrail logs show who accessed secrets
* **GitHub Secrets:** Audit logs show which workflows accessed secrets
* **Azure Key Vault:** Audit logs track all secret access

Check your infrastructure provider's audit logs for this visibility.

</details>

<details>

<summary>What if I accidentally use the same key in multiple environments?</summary>

**You should immediately rotate that key.**

Here's why: If one environment is compromised, all environments using that key are at risk.

**Steps to fix:**

1. Identify all systems using the key
2. Create separate keys for each environment/system
3. Deploy the new keys to each system
4. Verify each system is working with its new key
5. Revoke the shared key

This is why we recommend separate keys per environment from the start.

</details>

<details>

<summary>How do I know if my API key has been compromised?</summary>

Look for these signs in your activity logs:

* **Unusual request patterns:** Requests at times your app doesn't normally run
* **Unexpected endpoints:** Requests to data your app doesn't need
* **Geographic anomalies:** Requests from locations you don't operate in
* **Spike in failed authentication:** Someone trying random keys
* **Unexpected credit consumption:** Requests you didn't make

If you notice any of these patterns:

1. Check your application logs to see if the traffic came from your systems
2. If it didn't, revoke the key immediately
3. Investigate what data was accessed
4. Create a new key and redeploy
5. Review your security practices

</details>

<details>

<summary>Should I share my API key with partners or third parties?</summary>

**No—create a separate key for each partner instead.**

Here's why:

* **Isolation:** If a partner's system is compromised, only their key is affected
* **Audit trail:** You can see exactly which partner made which requests
* **Easy revocation:** You can disable a partner's access without affecting others
* **Rate limiting:** You can apply different limits per partner
* **Accountability:** Requests are clearly attributed to specific partners

**Instead:**

1. Create a new API key specifically for the partner
2. Document which key belongs to which partner
3. Share only that key (not your main key)
4. Monitor that key's activity closely
5. Revoke the key immediately if the partnership ends

</details>

<details>

<summary>How should I handle API keys for contractors or temporary team members?</summary>

**Create temporary keys and revoke them immediately when they leave.**

**Process:**

1. Create a key specifically for the contractor (e.g., `contractor-john-q3-2026`)
2. Set an expiration reminder for when the contractor's engagement ends
3. Share the key only with that contractor
4. Monitor the key's activity to ensure proper use
5. The day the contractor leaves, revoke the key in your Pullbay account
6. Verify no more requests are made with that key

This ensures former contractors can't access your data.

</details>

<details>

<summary>What credentials should I use in automated tests?</summary>

**Use a dedicated test API key** with a lower credit balance if possible.

**Approach:**

1. Create a test key (e.g., `ci-testing` or `dev-local-testing`)
2. Use that key in your test suite only
3. Keep the test key's credit balance low to limit damage if tests go wrong
4. Store the test key in your CI/CD secrets, not in code
5. Rotate test keys every few months
6. Monitor test key activity for unusual patterns

This isolates your test environment from production and prevents runaway tests from consuming all your credits.

</details>

***

**Have questions about API key security?** Contact Pullbay support or read our [Getting Started guide](broken://pages/dc418bd608f33fb865552fe0d19fda860fc07b06) for more information.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.pullbay.com/documentation/platform-best-practices/api-key-security.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
