# How to Integrate Third-Party APIs into Codex Plugins: A Complete Guide

> Learn to integrate third-party APIs into Codex plugins. This guide covers defining skills, using the rest request client, and registering your plugin for Codex discovery.

- Repository: [OpenAI/plugins](https://github.com/openai/plugins)
- Tags: how-to-guide
- Published: 2026-06-16

---

**Integrating third-party APIs into Codex plugins requires defining skill metadata in a [`SKILL.md`](https://github.com/openai/plugins/blob/main/SKILL.md) file, leveraging the reusable [`rest_request.py`](https://github.com/openai/plugins/blob/main/rest_request.py) client for HTTP operations, and registering the skill in the [`.codex-plugin/plugin.json`](https://github.com/openai/plugins/blob/main/.codex-plugin/plugin.json) manifest to enable Codex discovery and execution.**

Codex plugins extend AI capabilities by exposing *skills* that run inside a sandboxed Python interpreter. When you integrate third-party APIs into Codex plugins, you follow a standardized architecture that isolates network traffic to a single, well-tested module while declaring interfaces through structured metadata files.

## Understanding the Codex Plugin Architecture

Codex plugins use a directory-based structure where skills are self-contained units. The system relies on three core components working together: metadata declaration, a generic HTTP client, and manifest registration.

### Skill Metadata and Discovery

Each skill resides in its own subdirectory under `skills/` and must contain a [`SKILL.md`](https://github.com/openai/plugins/blob/main/SKILL.md) file. This file declares the skill's name, description, and input schema using YAML frontmatter. Codex reads this metadata to validate requests before execution.

### The Generic REST Client

Rather than importing heavy HTTP libraries for every integration, the repository provides [`rest_request.py`](https://github.com/openai/plugins/blob/main/rest_request.py). This compact client handles GET and POST requests, JSON versus text responses, pagination-style truncation, and optional raw-output saving. Skill authors import this module and call the `execute()` function with a standardized payload.

## Step-by-Step Integration Process

Follow this pattern to add any RESTful service to your Codex plugin without modifying the core framework.

### Step 1: Define the Skill Metadata (SKILL.md)

Create a [`SKILL.md`](https://github.com/openai/plugins/blob/main/SKILL.md) file in your skill directory that specifies the expected input schema. This document tells Codex what parameters the skill accepts, including `base_url`, `path`, and `params`.

```markdown
name: uniprot-skill
description: Query UniProt for protein information.
input_schema:
  type: object
  properties:
    base_url:
      const: https://rest.uniprot.org
    path:
      type: string
      enum:
        - uniprotkb/search
        - uniprotkb/{accession}
    params:
      type: object
      additionalProperties: true
  required:
    - base_url
    - path

```

The schema enforces type safety by requiring specific fields while allowing flexible query parameters through `additionalProperties: true`.

### Step 2: Reuse the Generic REST Client (rest_request.py)

Implement a Python wrapper that imports the shared client from [`scripts/rest_request.py`](https://github.com/openai/plugins/blob/main/scripts/rest_request.py). The wrapper reads the JSON payload from standard input, passes it to the `execute()` function, and returns the normalized result.

```python
#!/usr/bin/env python3
import json
import sys
from pathlib import Path

# Import the generic client used across the plugin ecosystem

sys.path.append(str(Path(__file__).parent / "scripts"))
from rest_request import execute, error

def main() -> int:
    try:
        payload = json.load(sys.stdin)
    except Exception as exc:
        sys.stdout.write(json.dumps(error("invalid_json", f"Could not parse JSON input: {exc}")))
        return 2

    # Delegate HTTP logic to the reusable client

    output = execute(payload)
    sys.stdout.write(json.dumps(output))
    return 0 if output.get("ok") else 1

if __name__ == "__main__":
    raise SystemExit(main())

```

The `execute()` function in [`rest_request.py`](https://github.com/openai/plugins/blob/main/rest_request.py) builds the full URL, merges headers, applies timeouts, and returns a normalized JSON document containing the HTTP status code, record count, and response data.

### Step 3: Register the Skill in the Plugin Manifest

Add the skill to [`.codex-plugin/plugin.json`](https://github.com/openai/plugins/blob/main/.codex-plugin/plugin.json) so Codex can discover it during initialization. The manifest maps skill names to their directory paths.

```json
{
  "name": "uniprot",
  "version": "0.1.0",
  "skills": [
    {
      "name": "uniprot-skill",
      "path": "skills/uniprot-skill"
    }
  ]
}

```

As shown in the Daloopa plugin example at [`plugins/daloopa/.codex-plugin/plugin.json`](https://github.com/openai/plugins/blob/main/plugins/daloopa/.codex-plugin/plugin.json), this registration enables Codex to locate and load your skill when users request it.

## Complete Integration Example: UniProt API

Here is a working implementation that queries the UniProt protein database using the patterns described above.

### Creating the SKILL.md Schema

The metadata file at [`plugins/life-science-research/skills/uniprot-skill/SKILL.md`](https://github.com/openai/plugins/blob/main/plugins/life-science-research/skills/uniprot-skill/SKILL.md) defines the contract between Codex and the external API. It specifies that `base_url` is constant while `path` and `params` vary by request.

### Implementing the Python Wrapper

The file [`plugins/life-science-research/skills/uniprot-skill/scripts/run_uniprot.py`](https://github.com/openai/plugins/blob/main/plugins/life-science-research/skills/uniprot-skill/scripts/run_uniprot.py) serves as the entry point. It performs minimal logic—parsing stdin and delegating to `rest_request.execute()`—making it reusable for other REST endpoints by changing only the input payload.

### Testing the Integration

Invoke the skill by piping a JSON payload that matches the schema:

```bash
cat <<'EOF' | python plugins/life-science-research/skills/uniprot-skill/scripts/run_uniprot.py
{
  "base_url": "https://rest.uniprot.org",
  "path": "uniprotkb/search",
  "method": "GET",
  "params": {
    "query": "gene:TP53 AND organism_id:9606",
    "fields": "accession,gene_names",
    "size": 5,
    "format": "json"
  },
  "response_format": "json",
  "max_items": 5,
  "max_depth": 2
}
EOF

```

The [`rest_request.py`](https://github.com/openai/plugins/blob/main/rest_request.py) client returns a normalized response:

```json
{
  "ok": true,
  "source": "rest-uniprot-org",
  "path": "uniprotkb/search",
  "method": "GET",
  "status_code": 200,
  "record_path": "results",
  "record_count_returned": 5,
  "record_count_available": 5,
  "truncated": false,
  "records": [
    { "accession": "P04637", "gene_names": "TP53" }
  ],
  "warnings": []
}

```

## Security and Safety Features

The integration pattern includes multiple safeguards. The [`rest_request.py`](https://github.com/openai/plugins/blob/main/rest_request.py) client validates input types, sanitizes strings, and enforces limits on returned data through `max_items` and `max_depth` parameters. When the `save_raw` option is enabled, the client stores unprocessed responses to temporary files rather than loading massive payloads into memory. Because all third-party network traffic routes through this single module, you can audit, unit-test, and secure integrations centrally without reviewing every individual skill implementation.

## Summary

- **Define metadata** in [`SKILL.md`](https://github.com/openai/plugins/blob/main/SKILL.md) to declare the skill's interface and input schema for Codex validation.
- **Reuse [`rest_request.py`](https://github.com/openai/plugins/blob/main/rest_request.py)** as the centralized HTTP client to handle GET/POST requests, response normalization, and pagination truncation across all third-party integrations.
- **Register skills** in [`.codex-plugin/plugin.json`](https://github.com/openai/plugins/blob/main/.codex-plugin/plugin.json) to enable discovery and loading by the Codex interpreter.
- **Implement thin wrappers** like [`run_uniprot.py`](https://github.com/openai/plugins/blob/main/run_uniprot.py) that parse stdin and delegate to the generic client, keeping skill-specific code minimal and testable.
- **Leverage built-in safety features** including input validation, string sanitization, and response size limits to maintain security within the sandboxed environment.

## Frequently Asked Questions

### What is the purpose of the SKILL.md file in Codex plugins?

The [`SKILL.md`](https://github.com/openai/plugins/blob/main/SKILL.md) file serves as the skill's contract with Codex, defining the name, description, and input schema using YAML frontmatter. Codex reads this file to validate user requests against the expected parameters before invoking the skill's Python wrapper, ensuring type safety and proper API usage.

### How does the generic REST client handle large API responses?

The [`rest_request.py`](https://github.com/openai/plugins/blob/main/rest_request.py) client implements pagination-style truncation using the `max_items` and `max_depth` parameters. It caps the number of returned records and nesting depth in JSON responses, preventing memory exhaustion. Optionally, setting `save_raw: true` streams the full response to a temporary file rather than loading it into the sandboxed interpreter's memory.

### Where does Codex discover available plugin skills?

Codex discovers skills by reading the [`.codex-plugin/plugin.json`](https://github.com/openai/plugins/blob/main/.codex-plugin/plugin.json) manifest file at the plugin root. This JSON file contains a `skills` array that maps skill names to their directory paths, allowing Codex to locate the corresponding [`SKILL.md`](https://github.com/openai/plugins/blob/main/SKILL.md) and executable scripts during initialization.

### Can I use custom HTTP libraries instead of rest_request.py?

While the architecture allows custom implementations, the repository design encourages reusing [`rest_request.py`](https://github.com/openai/plugins/blob/main/rest_request.py) to maintain consistency and security. This dependency-light client is used across dozens of integrations (UniProt, NCBI, Opentargets) and provides centralized handling of timeouts, headers, and response normalization that custom libraries would need to reimplement.