---
seo_title: REST, MCP, CLI or SDK: Which to Use | EnConvert
meta_desc: EnConvert exposes the same API through REST, an MCP server, a CLI, an n8n node, and ten SDKs. How the surfaces relate and when to pick each one.
keywords: rest vs mcp server, api access surfaces, mcp server vs rest api, cli vs sdk, one api key everywhere, enconvert integration surfaces, n8n node vs api, when to use an sdk
---

# One API, Five Ways to Call It

EnConvert is a single HTTP API at `https://api.enconvert.com`. The MCP server, the CLI, the n8n node and the ten SDKs are clients of that API rather than separate products. They hit the same endpoints with the same key.

## REST is the substrate

Every call ends up in the same shape: an HTTPS request to `https://api.enconvert.com` carrying an `X-API-Key` header. The gateway cannot tell which client sent it beyond the `User-Agent` string, and the only reason that string exists is attribution: every SDK sends `enconvert-sdk/<version> (<language>)` so traffic can be counted per language.

No client has an endpoint of its own. There is no API operation reachable through the CLI or the MCP server that you could not issue with `curl` and the pages on this site.

The reverse needs saying plainly, because it is where people get surprised: the clients wrap REST to different depths.

- The **CLI** is the widest. It has verbs for the conversion routes and for perceive, discover, lookup, distill and ingest, plus `enconvert api` as a gh-style passthrough that reaches anything it has no verb for yet.
- The **MCP server** registers 24 tools. It deliberately leaves out the webhook signing-secret endpoints (`GET /v2/ingest/webhook-secret` and its rotate counterpart), because a signing secret should not be readable by a model.
- The **n8n node** exposes 6 resources and 16 operations, shaped for workflow steps rather than for full API coverage.
- The **SDKs** cover the conversion endpoints and the V2 web endpoints in all ten languages.

When a client is narrower than the API, drop to REST for that one call. Mixing is fine. SDK calls and hand-rolled HTTP calls can share a project and a key without any special handling.

---

## One key, every surface

Every surface authenticates with the same private API key (`sk_...`), sent as the `X-API-Key` header. Generate one in the [dashboard](/dashboard) and it works in `curl`, in `enconvert auth login`, in an MCP config, in an n8n credential and in an SDK constructor. Rotating it rotates all of them.

What changes is only where the key is stored.

| Surface | Where the key lives | Environment override |
|---------|---------------------|----------------------|
| REST | Wherever your own code keeps secrets | -- |
| SDKs | Passed to the client constructor | -- |
| CLI | `credentials.toml`, file mode `0600` | `ENCONVERT_API_KEY` |
| MCP server | `~/.enconvert/config.json`, file mode `600` | `ENCONVERT_API_KEY` |
| n8n node | The `enconvertApi` credential inside n8n | -- |

<div class="alert alert-warning">
<strong>These surfaces need a private key.</strong> The CLI, the MCP server, the n8n node and the SDKs all expect <code>sk_...</code>. A public <code>pk_</code> key is for browser code that mints a short-lived JWT, and the n8n credential rejects <code>pk_</code> keys outright. See <a href="/docs/authentication">Authentication</a>.
</div>

Usage is shared as well. One monthly operations allowance covers every surface, and an operation costs the same whether it arrived from a Go program or from a terminal. See [Rate Limits and Quotas](/docs/reference/rate-limits.md).

---

## When to pick each

| Surface | What it is | Reach for it when |
|---------|-----------|-------------------|
| REST | The API itself: JSON and multipart bodies over HTTPS | You are writing application code, want no dependency, or your language has no SDK |
| SDK | Typed clients for ten languages | You want autocomplete on every parameter and timeout recovery you did not have to write |
| CLI | The `enconvert` binary, from Homebrew, Scoop, a shell installer or npm | You are writing a shell script, running a one-off job, or working in CI |
| MCP server | `@enconvert/mcp`, a local stdio server for AI assistants | A coding agent should decide for itself when to call the API |
| n8n node | `@enconvert/n8n-nodes-enconvert`, a community node | The workflow already lives in n8n and you want the result as n8n binary data |

Two of these choices are usually obvious. If a human is typing, that is the CLI; if a model is deciding, that is MCP. The REST-or-SDK question is the only one worth thinking about, and it turns on how much retry and polling code you want to own.

---

## The same read, three ways

Reading `https://example.com/pricing` into Markdown is one `POST /v2/perceive` request. Here it is as raw HTTP:

```bash
curl -X POST https://api.enconvert.com/v2/perceive \
  -H "X-API-Key: sk_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/pricing",
    "outputs": ["markdown"]
  }'
```

The CLI issues the same `POST /v2/perceive`. The bare command sends no `outputs`, so it gets the server default of Markdown plus the inline structured block:

```bash
enconvert perceive https://example.com/pricing
```

Through MCP you do not write the call at all. You ask the assistant to read the page, it picks the `perceive_url` tool, and the arguments that matter come out as:

```json
{
  "name": "perceive_url",
  "arguments": {
    "url": "https://example.com/pricing",
    "outputs": ["markdown"]
  }
}
```

<div class="alert alert-info">
<strong>Same request, same bill.</strong> Each of the three renders the page once and costs one operation. The MCP server does one extra thing on top: it fetches the finished Markdown artifact and inlines the text in the tool result (up to roughly 256 KB), so the assistant can read the page without a second fetch.
</div>

---

## Where to go next

Setup lives in the integration guides, one page per surface:

- **[Integrations](/docs/guides/integrations.md)** is the hub, and also covers the embeddable web widgets.
- **[MCP Setup](/docs/guides/integrations/mcp-setup.md)** installs `@enconvert/mcp` into Claude Code, Cursor, Windsurf and six other clients with `npx @enconvert/mcp setup`.
- **[n8n](/docs/guides/integrations/n8n.md)** installs the community node and walks through the six resources.
- **[CLI](/docs/guides/integrations/cli.md)** covers install, `enconvert auth login`, the scripting flags and the exit codes.
- **[SDKs](/docs/guides/integrations/sdks.md)** lists all ten packages with a page each.

For the API itself, [Endpoints](/docs/endpoints.md) is the reference and [Authentication](/docs/authentication.md) explains the two key types.

---

## Frequently asked questions

### Is the MCP server a different API from the REST API?

No. `@enconvert/mcp` is a local stdio process that calls the same public REST endpoints documented on this site, using your private API key. Every tool maps to a `/v1/convert/*` or `/v2/*` request, so it draws on the same quota and returns the same errors.

### Do I need separate API keys for the CLI, MCP and my application?

No. One private key (`sk_...`) authenticates all of them, and you can reuse the same key across surfaces. Separate keys are still useful if you want to revoke one surface without touching the others, for example a CI key you can rotate independently of your laptop.

### Can I use a public pk_ key with the CLI or an SDK?

No. Public keys exist for browser code, where they mint a short-lived JWT instead of being sent directly. The CLI, the MCP server, the n8n node and the SDKs all expect a private `sk_` key, and the n8n credential rejects a `pk_` key at validation time.

### Is anything available only through the SDKs or only through the CLI?

No API operation is. What the SDKs add is client-side: type-safe options, streaming downloads to disk, and automatic fallback to `GET /v1/convert/status/{job_id}` polling when a long conversion outlasts the proxy timeout. You can write all of that against raw REST yourself.

### Which surface should I use inside a CI pipeline?

The CLI, with `ENCONVERT_API_KEY` set from your CI secret store and `--no-input` so a prompt can never block the run. Its exit codes are a stable published contract, so a failed conversion fails the step without any output parsing.
