> ## Documentation Index
> Fetch the complete documentation index at: https://docs.memeperfect.io/llms.txt
> Use this file to discover all available pages before exploring further.

# APIs

> External API reference for strategies, backtests, strategy evaluations, notifications, performance, and webhook configuration.

Use this page to integrate directly with MemePerfect's external API.

If you are integrating for the first time, start with the end-to-end [API Walkthrough](/developers/api-walkthrough), then use this page as endpoint reference.

<Frame caption="Use the external API to manage Strategies and consume alert outcomes from your own systems.">
  <img src="https://mintcdn.com/memeperfect/VFqzC6TIyzPPPtDe/images/api-access.png?fit=max&auto=format&n=VFqzC6TIyzPPPtDe&q=85&s=6bfb225a9bbe50f5c2c46c4ba87c2a6d" alt="Illustration for developer API key section" width="1570" height="800" data-path="images/api-access.png" />
</Frame>

## Base URL

```text theme={null}
https://api.memeperfect.io/api/external/v1
```

## Getting an API key

1. Open **Settings** in the app.
2. Go to **API Access**.

<Frame caption="Generate your API key from Settings.">
  <img src="https://mintcdn.com/memeperfect/VFqzC6TIyzPPPtDe/images/generate-api-key.png?fit=max&auto=format&n=VFqzC6TIyzPPPtDe&q=85&s=017c8fd25cdd052fa89423dfde621b7e" alt="Developer API key generation screen" width="1632" height="462" data-path="images/generate-api-key.png" />
</Frame>

3. Create or regenerate your API key.

<Frame caption="The full key is only shown at generation time.">
  <img src="https://mintcdn.com/memeperfect/VFqzC6TIyzPPPtDe/images/key.jpg?fit=max&auto=format&n=VFqzC6TIyzPPPtDe&q=85&s=771cbce8d08c09f4d998d5b00e08ccfb" alt="Generated API key shown in the dashboard" width="1622" height="946" data-path="images/key.jpg" />
</Frame>

4. Store it securely.

<Warning>Never expose API keys in browser code or public repositories.</Warning>

## Authentication

The API accepts one of these header formats:

```http theme={null}
X-API-Key: mpk_your_api_key_here
```

```http theme={null}
Authorization: ApiKey mpk_your_api_key_here
```

<Info>
  `X-MP-API-Key` is not accepted. Use `X-API-Key` or `Authorization: ApiKey
      ...`.
</Info>

## Rate limits and plan limits

Limits are plan-based and enforced per API key:

* `PRO`: 60 requests per minute, up to 25 active Strategies
* `DEGEN`: 300 requests per minute, up to 50 active Strategies
* Backtests: `PRO` gets 25 jobs per UTC day with a 7-day lookback; `DEGEN` gets unlimited daily jobs with a 14-day lookback

Check current limits with `GET /me`.

## Common error shape

Validation and auth errors use this structure:

```json theme={null}
{
  "statusCode": 401,
  "message": "API key required. Provide via X-API-Key header or Authorization: ApiKey <key>",
  "error": "Unauthorized"
}
```

## Endpoint reference

Lifecycle order:

1. `GET /me`
2. `POST /strategies`
3. `POST /strategies/:id/activate`
4. `GET /strategy-evaluations`
5. `POST /backtests` and `GET /backtests/:id/results`
6. `GET /notifications` and `GET /notifications/:id`
7. `GET /notifications/performance` or async performance jobs
8. `PUT /webhook`
9. `POST /strategies/:id/deactivate`

### `GET /me`

Returns authenticated user plan and limits.

```bash theme={null}
curl -X GET 'https://api.memeperfect.io/api/external/v1/me' \
  -H 'X-API-Key: YOUR_API_KEY'
```

```json theme={null}
{
  "userId": "4fcb13dc-2f0c-49bb-97a3-2f2f7ac0f80f",
  "plan": "PRO",
  "limits": {
    "strategies": {
      "total": null,
      "active": 25
    },
    "rateLimit": {
      "requestsPerMinute": 60
    }
  }
}
```

### `GET /strategies`

Returns all Strategies for the authenticated user and `activeCount`.

```bash theme={null}
curl -X GET 'https://api.memeperfect.io/api/external/v1/strategies' \
  -H 'X-API-Key: YOUR_API_KEY'
```

```json theme={null}
{
  "strategies": [
    {
      "id": "b1b3f6f3-84be-4c7c-8e44-4cb4a58bad55",
      "name": "Momentum Strategy",
      "description": "Filters for high social acceleration",
      "userId": "4fcb13dc-2f0c-49bb-97a3-2f2f7ac0f80f",
      "triggers": [
        {
          "id": "t1",
          "eventType": "new_token_created",
          "enabled": true
        }
      ],
      "dealbreakers": [],
      "ruleGroups": [
        {
          "id": "security",
          "name": "Security",
          "logic": "AND",
          "rules": []
        },
        {
          "id": "market",
          "name": "Market",
          "logic": "AND",
          "rules": []
        },
        {
          "id": "socials",
          "name": "Socials",
          "logic": "AND",
          "rules": []
        },
        {
          "id": "ai",
          "name": "AI",
          "logic": "AND",
          "rules": []
        }
      ],
      "strict": false,
      "alertCooldownMins": 30,
      "isActive": true,
      "isGlobal": false,
      "matching": {
        "enabled": true,
        "minPercent": 80
      },
      "createdAt": "2026-03-09T08:22:11.318Z",
      "updatedAt": "2026-03-09T08:22:11.318Z"
    }
  ],
  "activeCount": 1
}
```

### `POST /strategies`

Creates a new Strategy.

Required body fields:

* `name` (string)
* `triggers` (array)
* `rules` (array)
* `isActive` (boolean)

Send rules as one flat list. MemePerfect organizes them into:

* dealbreakers
* product rule groups such as `Security`, `Market`, `Volume/Activity`, `Clusters`, `Socials`, and `AI`

Rule item shape:

* `rule` (string, required)
* `dealbreaker` (boolean, required)
* `enabled` (boolean, optional. `enabled: false` skips any rule type)
* `min` / `max` (number, numeric rules only)
* `value` (string, enum rules only: `risk_level`, `launch_platform`)
* `operator` (string, enum rules only: `equals` or `not_equals`; defaults to `equals`)
* `config` (object, advanced market, volume/activity, and wallet-quality rules only)
* `clusterConfig` (object, coordinated wallet cluster rules only)

Advanced rule examples:

```json theme={null}
{ "rule": "volume_24h", "dealbreaker": false, "config": { "windows": { "5m": 5000, "1h": 10000, "6h": 25000, "24h": 50000 } } }
```

```json theme={null}
{ "rule": "buy_sell_ratio_window", "dealbreaker": false, "config": { "window": "1h", "buyPressure": "moderate" } }
```

```json theme={null}
{ "rule": "validated_volume_spike", "dealbreaker": false, "config": { "window": "5m", "volumeSpike": "very_strong" } }
```

```json theme={null}
{ "rule": "quality_wallet_count", "dealbreaker": false, "config": { "minWallets": 5, "walletTypes": ["smart_wallet", "kol", "whale", "profitable_trader"] } }
```

```json theme={null}
{ "rule": "quality_wallet_total_value", "dealbreaker": false, "config": { "minTotalValueUsd": 5000, "walletTypes": ["smart_wallet", "whale"] } }
```

```json theme={null}
{ "rule": "quality_wallet_min_winrate", "dealbreaker": false, "config": { "minWinratePct": 60, "minWalletsAboveThreshold": 3, "walletTypes": ["smart_wallet", "profitable_trader"] } }
```

```json theme={null}
{ "rule": "coordinated_wallet_cluster", "dealbreaker": false, "clusterConfig": { "maxTotalHeldPct": 20, "maxSingleClusterPct": 10, "maxClusterRisk": "high" } }
```

<Warning>
  These rules cannot be submitted as dealbreakers:

  * Website Shows CA
  * Website Contains \$Token Name
  * Website Content Validity Score
  * X Link Contains CA
  * X Link Contains \$Token Name
  * X Views
  * X Followers
  * X Community Size
  * X Community Views
  * X (Twitter) Content Validity Score
  * Coordinated Wallet Cluster
  * Quality Wallets
  * Quality Wallet Buy Value
  * Quality Wallet Win Rate
</Warning>

Optional trigger config map:

* `triggerConfigs.twitter_mention_direct.tagId` is required for `twitter_mention_direct`; use `"all"` or a Twitter tag id.
* `triggerConfigs.tweet_metadata_match.tagId` is required for `tweet_metadata_match`; use `"all"` or a Twitter tag id.
* `triggerConfigs.followed_dev_new_token_created.scope` is optional and accepts `"all"`, `"tag"`, or `"wallet"`.
* `triggerConfigs.followed_dev_new_token_created.tagId` is required when `scope` is `"tag"`; use a dev tag id from `GET /devs/tags`.
* `triggerConfigs.followed_dev_new_token_created.developerAddress` is required when `scope` is `"wallet"`; the wallet must be in your followed developers list.

If `triggerConfigs.followed_dev_new_token_created` is omitted, the followed-dev trigger behaves as `scope: "all"`.

Important strategy-level options:

* `strict` (optional, boolean)
* `alertCooldownMins` (optional, number `0` to `1440`)

Allowed trigger `eventType` values:

* `new_token_created`
* `followed_dev_new_token_created`
* `twitter_mention_direct`
* `tweet_metadata_match`
* `token_almost_graduated`
* `token_graduated`

```bash theme={null}
curl -X POST 'https://api.memeperfect.io/api/external/v1/strategies' \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "E2E Test Strategy",
    "strict": true,
    "alertCooldownMins": 30,
    "triggers": ["new_token_created"],
    "rules": [
      { "rule": "is_honeypot", "dealbreaker": true, "enabled": true },
      { "rule": "risk_level", "dealbreaker": true, "value": "LOW" },
      { "rule": "launch_platform", "dealbreaker": false, "operator": "not_equals", "value": "pump_fun" },
      { "rule": "liquidity_usd", "dealbreaker": false, "min": 10000 },
      { "rule": "market_cap", "dealbreaker": false, "max": 1500000 },
      { "rule": "volume_24h", "dealbreaker": false, "config": { "windows": { "5m": 5000, "1h": 10000, "6h": 25000, "24h": 50000 } } },
      { "rule": "buy_sell_ratio_window", "dealbreaker": false, "config": { "window": "1h", "buyPressure": "moderate" } },
      { "rule": "quality_wallet_count", "dealbreaker": false, "config": { "minWallets": 5, "walletTypes": ["smart_wallet", "kol", "whale", "profitable_trader"] } },
      { "rule": "has_twitter", "dealbreaker": false, "enabled": true }
    ],
    "isActive": false
  }'
```

Followed developer trigger examples:

```bash theme={null}
curl -X POST 'https://api.memeperfect.io/api/external/v1/strategies' \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "All followed dev launches",
    "triggers": ["followed_dev_new_token_created"],
    "triggerConfigs": {
      "followed_dev_new_token_created": {
        "scope": "all"
      }
    },
    "rules": [
      { "rule": "is_honeypot", "dealbreaker": true, "enabled": true },
      { "rule": "liquidity_usd", "dealbreaker": false, "min": 10000 }
    ],
    "isActive": true
  }'
```

```json theme={null}
{
  "triggers": ["followed_dev_new_token_created"],
  "triggerConfigs": {
    "followed_dev_new_token_created": {
      "scope": "tag",
      "tagId": "b0d6a1d8-6e6c-4f80-9a61-5b8a3c4b2a11"
    }
  }
}
```

```json theme={null}
{
  "triggers": ["followed_dev_new_token_created"],
  "triggerConfigs": {
    "followed_dev_new_token_created": {
      "scope": "wallet",
      "developerAddress": "7xKXExampleDeveloperWallet"
    }
  }
}
```

Twitter trigger example:

```bash theme={null}
curl -X POST 'https://api.memeperfect.io/api/external/v1/strategies' \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Twitter mentions by tag",
    "triggers": ["twitter_mention_direct"],
    "triggerConfigs": {
      "twitter_mention_direct": {
        "tagId": "all"
      }
    },
    "rules": [
      { "rule": "is_honeypot", "dealbreaker": true, "enabled": true },
      { "rule": "liquidity_usd", "dealbreaker": false, "min": 10000 }
    ],
    "isActive": true
  }'
```

<Info>
  The response may include `dealbreakers` and `ruleGroups`; these are generated
  from your `rules[]` payload.
</Info>

```json theme={null}
{
  "strategy": {
    "id": "9a46ddf3-98fd-4cf5-8fd0-022421741c35",
    "name": "E2E Test Strategy",
    "description": null,
    "userId": "4fcb13dc-2f0c-49bb-97a3-2f2f7ac0f80f",
    "triggers": [
      {
        "id": "t1",
        "eventType": "new_token_created",
        "enabled": true
      }
    ],
    "dealbreakers": [
      {
        "id": "d1f6f6fe-b6e7-4f65-a5ed-4cddf0f20274",
        "ruleType": "is_honeypot",
        "operator": "is",
        "value": false,
        "enabled": true
      },
      {
        "id": "4155b2c2-6459-4b09-a406-f6f45887ef4a",
        "ruleType": "risk_level",
        "operator": "equals",
        "value": "LOW",
        "enabled": true
      }
    ],
    "ruleGroups": [
      {
        "id": "b9793be5-2667-4d64-9d03-e8f89b022f75",
        "name": "Market",
        "logic": "AND",
        "rules": [
          {
            "id": "ad59ec06-4f8b-490e-a7f6-78de34a37834",
            "ruleType": "liquidity_usd",
            "operator": "greater_than",
            "value": 10000,
            "enabled": true
          },
          {
            "id": "95f08d8c-fb80-4c7f-a1ce-85ce6bb005ca",
            "ruleType": "market_cap",
            "operator": "less_than",
            "value": 1500000,
            "enabled": true
          }
        ]
      },
      {
        "id": "d967eeb9-b7d0-4aaf-a84a-45b378e56f2d",
        "name": "Socials",
        "logic": "AND",
        "rules": [
          {
            "id": "6d04ecb7-650c-4c66-94e6-0f4dd44bc45d",
            "ruleType": "has_twitter",
            "operator": "is",
            "value": true,
            "enabled": true
          }
        ]
      }
    ],
    "strict": true,
    "alertCooldownMins": 30,
    "isActive": false,
    "isGlobal": false,
    "createdAt": "2026-03-09T08:25:58.221Z",
    "updatedAt": "2026-03-09T08:25:58.221Z"
  }
}
```

### `GET /strategies/:id`

Returns one Strategy by UUID.

```bash theme={null}
curl -X GET 'https://api.memeperfect.io/api/external/v1/strategies/9a46ddf3-98fd-4cf5-8fd0-022421741c35' \
  -H 'X-API-Key: YOUR_API_KEY'
```

```json theme={null}
{
  "strategy": {
    "id": "9a46ddf3-98fd-4cf5-8fd0-022421741c35",
    "name": "E2E Test Strategy",
    "description": null,
    "userId": "4fcb13dc-2f0c-49bb-97a3-2f2f7ac0f80f",
    "triggers": [
      {
        "id": "t1",
        "eventType": "new_token_created",
        "enabled": true
      }
    ],
    "dealbreakers": [
      {
        "id": "d1f6f6fe-b6e7-4f65-a5ed-4cddf0f20274",
        "ruleType": "is_honeypot",
        "operator": "is",
        "value": false,
        "enabled": true
      },
      {
        "id": "4155b2c2-6459-4b09-a406-f6f45887ef4a",
        "ruleType": "risk_level",
        "operator": "equals",
        "value": "LOW",
        "enabled": true
      }
    ],
    "ruleGroups": [
      {
        "id": "b9793be5-2667-4d64-9d03-e8f89b022f75",
        "name": "Market",
        "logic": "AND",
        "rules": [
          {
            "id": "ad59ec06-4f8b-490e-a7f6-78de34a37834",
            "ruleType": "liquidity_usd",
            "operator": "greater_than",
            "value": 10000,
            "enabled": true
          },
          {
            "id": "95f08d8c-fb80-4c7f-a1ce-85ce6bb005ca",
            "ruleType": "market_cap",
            "operator": "less_than",
            "value": 1500000,
            "enabled": true
          }
        ]
      },
      {
        "id": "d967eeb9-b7d0-4aaf-a84a-45b378e56f2d",
        "name": "Socials",
        "logic": "AND",
        "rules": [
          {
            "id": "6d04ecb7-650c-4c66-94e6-0f4dd44bc45d",
            "ruleType": "has_twitter",
            "operator": "is",
            "value": true,
            "enabled": true
          }
        ]
      }
    ],
    "strict": true,
    "alertCooldownMins": 30,
    "isActive": false,
    "createdAt": "2026-03-09T08:25:58.221Z",
    "updatedAt": "2026-03-09T08:25:58.221Z"
  }
}
```

### `PATCH /strategies/:id`

Partially updates a Strategy. Uses the same schema as `POST /strategies`, but all fields are optional.

Replacement semantics:

* If `rules` is included, MemePerfect fully replaces the strategy's saved rules.
* If `rules` is explicitly `[]`, MemePerfect clears the strategy's saved rules.
* If `triggers` is included, MemePerfect fully replaces the strategy's saved triggers.
* If `rules` or `triggers` are omitted, existing values remain unchanged.

```bash theme={null}
curl -X PATCH 'https://api.memeperfect.io/api/external/v1/strategies/9a46ddf3-98fd-4cf5-8fd0-022421741c35' \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Updated E2E Strategy",
    "description": "Updated desc"
  }'
```

```json theme={null}
{
  "strategy": {
    "id": "9a46ddf3-98fd-4cf5-8fd0-022421741c35",
    "name": "Updated E2E Strategy",
    "description": "Updated desc",
    "isActive": false
  }
}
```

Clear all rules example:

```bash theme={null}
curl -X PATCH 'https://api.memeperfect.io/api/external/v1/strategies/9a46ddf3-98fd-4cf5-8fd0-022421741c35' \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "rules": []
  }'
```

### `POST /strategies/:id/activate`

Activates a Strategy.

```bash theme={null}
curl -X POST 'https://api.memeperfect.io/api/external/v1/strategies/9a46ddf3-98fd-4cf5-8fd0-022421741c35/activate' \
  -H 'X-API-Key: YOUR_API_KEY'
```

```json theme={null}
{
  "strategy": {
    "id": "9a46ddf3-98fd-4cf5-8fd0-022421741c35",
    "isActive": true
  }
}
```

### `POST /strategies/:id/deactivate`

Deactivates a Strategy.

```bash theme={null}
curl -X POST 'https://api.memeperfect.io/api/external/v1/strategies/9a46ddf3-98fd-4cf5-8fd0-022421741c35/deactivate' \
  -H 'X-API-Key: YOUR_API_KEY'
```

```json theme={null}
{
  "strategy": {
    "id": "9a46ddf3-98fd-4cf5-8fd0-022421741c35",
    "isActive": false
  }
}
```

### `DELETE /strategies/:id`

Deletes a Strategy.

```bash theme={null}
curl -X DELETE 'https://api.memeperfect.io/api/external/v1/strategies/9a46ddf3-98fd-4cf5-8fd0-022421741c35' \
  -H 'X-API-Key: YOUR_API_KEY'
```

```json theme={null}
{
  "success": true
}
```

### `GET /strategy-evaluations`

Lists persisted Strategy evaluation snapshots for the authenticated user. Use this to review matched and failed evaluations without calling the current market-cap, ATH, or performance endpoints.

<Info>
  This endpoint is optimized for recent evaluation history. Prefer `hasMore`
  and `nextCursor` for pagination. The exact `total` is returned as `null`.
</Info>

<Info>
  Evaluation history only returns records from the last day. The `from` filter
  is limited to that retention window.
</Info>

Query parameters:

* `page` (optional, default `1`; used when `cursor` is not provided)
* `pageSize` (optional, default `50`, max `100`)
* `limit` (optional alias for `pageSize`; `pageSize` wins when both are sent)
* `cursor` (optional opaque value returned as `nextCursor`; use with the same filters and sort)
* `sortBy` (`createdAt` | `matched` | `evaluationTimeMs` | `tokenAddress` | `strategyId` | `mcap` | `liquidity`, optional)
* `sortDir` (`asc` | `desc`, optional)
* `matched` (`true` | `false`, optional)
* `strategyId` (Strategy UUID, optional)
* `address` (token address exact match, or partial match when `%` is present, optional)
* `datetime` (ISO datetime one-minute window, or `YYYY-MM-DD` UTC day window, optional)
* `dealbreakerFailed` (`true` | `false`, optional)
* `failedGroupIds` (CSV or repeated query parameter; matches exactly that failed-group set, optional)
* `from` / `to` (strict ISO 8601 `createdAt` bounds, optional; `from` must be before or equal to `to`)

```bash theme={null}
curl -X GET 'https://api.memeperfect.io/api/external/v1/strategy-evaluations?page=1&matched=true&sortBy=createdAt&sortDir=desc' \
  -H 'X-API-Key: YOUR_API_KEY'
```

```json theme={null}
{
  "items": [
    {
      "evaluationId": "79a42f9c-2338-4835-bddc-4137a42ca678",
      "strategyId": "9a46ddf3-98fd-4cf5-8fd0-022421741c35",
      "strategyName": "E2E Test Strategy",
      "userId": "4fcb13dc-2f0c-49bb-97a3-2f2f7ac0f80f",
      "tokenAddress": "So11111111111111111111111111111111111111112",
      "eventType": "new_token_created",
      "matched": true,
      "matchDetails": {
        "matchedGroups": ["market"]
      },
      "failedRules": [],
      "baselineAt": "2026-05-07T10:00:00.000Z",
      "baselinePrice": 0.001,
      "baselineMcap": 100000,
      "failureReason": null,
      "marketAtEval": {
        "liquidityUsd": 25000
      },
      "riskAtEval": {
        "riskLevel": "LOW"
      },
      "sourceContext": {
        "source": "realtime"
      },
      "createdAt": "2026-05-07T10:00:00.000Z",
      "debugTrail": []
    }
  ],
  "page": 1,
  "pageSize": 50,
  "total": null,
  "hasMore": true,
  "nextCursor": "eyJ2IjoxLCJzb3J0QnkiOiJjcmVhdGVkQXQiLCJzb3J0RGlyIjoiREVTQyIsImlkIjoiNzlhNDJmOWMtMjMzOC00ODM1LWJkZGMtNDEzN2E0MmNhNjc4In0"
}
```

This endpoint returns stored evaluation state only. It does not include `currentMcap`, `athPrice`, `athMcap`, `athAt`, `currentMultiple`, or `athMultiple`, and it does not expose `/mcaps`, `/performance`, or `/analyze` under the external Strategy Evaluations API.

To fetch the next page, pass the previous response's `nextCursor` with the same
filters and sort:

```bash theme={null}
curl -X GET 'https://api.memeperfect.io/api/external/v1/strategy-evaluations?pageSize=50&cursor=NEXT_CURSOR_FROM_PREVIOUS_RESPONSE&matched=true&sortBy=createdAt&sortDir=desc' \
  -H 'X-API-Key: YOUR_API_KEY'
```

### `GET /backtests`

Lists async backtest jobs for the authenticated user.

Query parameters:

* `page` (optional, default `1`; fixed page size `20`)

```bash theme={null}
curl -X GET 'https://api.memeperfect.io/api/external/v1/backtests?page=1' \
  -H 'X-API-Key: YOUR_API_KEY'
```

```json theme={null}
{
  "items": [
    {
      "id": "39bcdd52-7e7e-4854-a3d7-b49c59b6fd21",
      "status": "COMPLETED",
      "strategyId": "9a46ddf3-98fd-4cf5-8fd0-022421741c35",
      "strategyName": "E2E Test Strategy",
      "from": "2026-05-16T00:00:00.000Z",
      "to": "2026-05-23T00:00:00.000Z",
      "labelDefinition": {
        "threshold": 2
      },
      "options": {
        "eventType": "new_token_created",
        "strictOverride": true
      },
      "metrics": {
        "tested": 240,
        "positives": 18,
        "winnersNow": 6,
        "precision": 0.33,
        "recall": 1,
        "f1": 0.5
      },
      "createdAt": "2026-05-23T10:15:00.000Z",
      "updatedAt": "2026-05-23T10:17:40.000Z",
      "error": null
    }
  ],
  "page": 1,
  "pageSize": 20,
  "total": 1,
  "hasMore": false
}
```

`metrics.winnersNow` is retained for response compatibility. For disk-backed
backtests it represents winners inside the effective snapshot window, not a live
market lookup.

### `POST /backtests`

Queues a backtest job for one of your Strategies. The API stores a strategy snapshot when the job is created, so later edits to the live Strategy do not change that backtest run.

Backtests replay hourly disk snapshots built from completed UTC hours. If your requested window overlaps available snapshot coverage, the returned job `from` and `to` are clamped to the available slice. If there is no overlap, the API returns `400` with the available range.

<Info>
  `PRO` users can create 25 backtests per UTC day with a 7-day lookback. `DEGEN`
  users have unlimited daily backtests with a 14-day lookback.
</Info>

Required body fields:

* `strategyId` (Strategy UUID owned by the API-key user)
* `from` / `to` (ISO 8601 datetimes)
* `labelDefinition.threshold` (number; snapshot-window winner multiple threshold)
* `options` (object; send `{}` when you do not need options)

Optional `options` fields:

* `eventType` (string)
* `strictOverride` (boolean)

```bash theme={null}
curl -X POST 'https://api.memeperfect.io/api/external/v1/backtests' \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "strategyId": "9a46ddf3-98fd-4cf5-8fd0-022421741c35",
    "from": "2026-05-16T00:00:00.000Z",
    "to": "2026-05-23T00:00:00.000Z",
    "labelDefinition": {
      "threshold": 2
    },
    "options": {
      "eventType": "new_token_created",
      "strictOverride": true
    }
  }'
```

```json theme={null}
{
  "id": "39bcdd52-7e7e-4854-a3d7-b49c59b6fd21",
  "status": "QUEUED",
  "strategyId": "9a46ddf3-98fd-4cf5-8fd0-022421741c35",
  "from": "2026-05-16T00:00:00.000Z",
  "to": "2026-05-23T00:00:00.000Z",
  "labelDefinition": {
    "threshold": 2
  },
  "options": {
    "eventType": "new_token_created",
    "strictOverride": true
  }
}
```

### `GET /backtests/:id`

Returns one backtest job owned by the authenticated user.

```bash theme={null}
curl -X GET 'https://api.memeperfect.io/api/external/v1/backtests/39bcdd52-7e7e-4854-a3d7-b49c59b6fd21' \
  -H 'X-API-Key: YOUR_API_KEY'
```

Missing or cross-user backtest IDs return `404`.

### `GET /backtests/:id/results`

Returns paginated stored backtest result rows plus `todayCount`.

Backtest results store matches and missed winners only:

* matched tokens, including true positives and false positives
* non-matched tokens whose snapshot-window `currentMultiple` reaches the threshold, meaning missed winners

Non-matched non-winners are counted in job metrics but are not stored as result rows. The response `total` is the stored-row count after filters; use the job `metrics.tested` field for the full evaluated-token count.

Query parameters:

* `page` (optional, default `1`)
* `pageSize` (optional, default `50`, max `200`)
* `matched` (`true` | `false` | `all`, optional)
* `winnersInWindow` (`true` | `false` | `all`, optional; preferred)
* `winnersNow` (`true` | `false` | `all`, optional; backward-compatible alias for `winnersInWindow`)
* `minMultiple` / `maxMultiple` (optional)
* `token` (optional token-address search)
* `sortBy` (`evaluationTimestamp` | `multiple` | `matched`, optional)
* `sortDir` (`asc` | `desc`, optional)

`sortBy=multiple`, `minMultiple`, `maxMultiple`, `winnersInWindow`, and `winnersNow` use `currentMultiple`, which is calculated from the token's highest market cap during the backtest window compared with its market cap when it was evaluated. It is not a live Birdeye multiple.

```bash theme={null}
curl -X GET 'https://api.memeperfect.io/api/external/v1/backtests/39bcdd52-7e7e-4854-a3d7-b49c59b6fd21/results?page=1&pageSize=50&winnersInWindow=true&sortBy=multiple&sortDir=desc' \
  -H 'X-API-Key: YOUR_API_KEY'
```

```json theme={null}
{
  "items": [
    {
      "id": "b83abde1-a2b7-4d21-8188-7e819f61dfd9",
      "jobId": "39bcdd52-7e7e-4854-a3d7-b49c59b6fd21",
      "tokenAddress": "So11111111111111111111111111111111111111112",
      "evaluationTimestamp": "2026-05-22T14:02:00.000Z",
      "matched": true,
      "currentMultiple": 2.4,
      "multiples": {
        "0": 2.4
      },
      "mcaps": {
        "0": 240000
      },
      "matchDetails": {},
      "snapshot": {
        "createdAt": "2026-05-22T14:02:00.000Z",
        "strategyId": "9a46ddf3-98fd-4cf5-8fd0-022421741c35",
        "strategyName": "E2E Test Strategy",
        "matched": true,
        "failureReason": null
      }
    }
  ],
  "page": 1,
  "pageSize": 50,
  "total": 1,
  "hasMore": false,
  "todayCount": 3
}
```

### `GET /twitter/handles`

Read-only list of tracked Twitter handles for the authenticated user.

Query parameters:

* `search` (optional)
* `tagId` (optional)
* `status` (`active` | `inactive`, optional)
* `sortBy` (`handle` | `addedAt` | `lastUpdated`, optional)
* `sortOrder` (`asc` | `desc`, optional)

```bash theme={null}
curl -X GET 'https://api.memeperfect.io/api/external/v1/twitter/handles?status=active&sortBy=addedAt&sortOrder=desc' \
  -H 'X-API-Key: YOUR_API_KEY'
```

```json theme={null}
{
  "handles": [
    {
      "id": "9a2e931b-c70f-4cde-aeac-15b7f2f7df91",
      "handle": "@alpha",
      "status": "active",
      "tags": [
        { "id": "40153fe3-a61a-4e8f-868f-fd6bdccfd6b2", "name": "Smart money" }
      ],
      "addedAt": "2026-03-11T10:00:00.000Z",
      "lastUpdated": "2026-03-11T10:00:00.000Z"
    }
  ]
}
```

### `GET /twitter/tags`

Read-only Twitter tags. Returns both user and platform tags by default.

Query parameters:

* `scope` (`all` | `user` | `platform`, default `all`)
* `includeCounts` (boolean, default `true`)
* `sortBy` (`name` | `createdAt` | `handleCount`, default `name`)
* `sortOrder` (`asc` | `desc`, default `asc`)

```bash theme={null}
curl -X GET 'https://api.memeperfect.io/api/external/v1/twitter/tags?scope=all&includeCounts=true' \
  -H 'X-API-Key: YOUR_API_KEY'
```

```json theme={null}
{
  "tags": [
    {
      "id": "40153fe3-a61a-4e8f-868f-fd6bdccfd6b2",
      "name": "Smart money",
      "isPlatformTag": false,
      "handleCount": 12
    }
  ]
}
```

### `GET /devs/my`

Lists your followed developers.

Query parameters:

* `page` (optional, default `1`)
* `limit` (optional, default `20`)
* `isActive` (optional)

```bash theme={null}
curl -X GET 'https://api.memeperfect.io/api/external/v1/devs/my?page=1&limit=20&isActive=true' \
  -H 'X-API-Key: YOUR_API_KEY'
```

```json theme={null}
{
  "developers": [],
  "total": 0,
  "page": 1,
  "totalPages": 0,
  "activeCount": 0
}
```

### `GET /devs/tags`

Returns your developer tags.

```bash theme={null}
curl -X GET 'https://api.memeperfect.io/api/external/v1/devs/tags' \
  -H 'X-API-Key: YOUR_API_KEY'
```

### `GET /devs/:address/tags`

Returns tag IDs currently assigned to a followed dev wallet.

```bash theme={null}
curl -X GET 'https://api.memeperfect.io/api/external/v1/devs/DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263/tags' \
  -H 'X-API-Key: YOUR_API_KEY'
```

```json theme={null}
{
  "tagIds": ["b0d6a1d8-6e6c-4f80-9a61-5b8a3c4b2a11"]
}
```

### `POST /devs/:address/add`

Adds a wallet to your followed developers list.

```bash theme={null}
curl -X POST 'https://api.memeperfect.io/api/external/v1/devs/DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263/add' \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "alias": "High conviction dev"
  }'
```

### `PUT /devs/:address/update`

Updates alias and/or active status for a followed dev wallet.

```bash theme={null}
curl -X PUT 'https://api.memeperfect.io/api/external/v1/devs/DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263/update' \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "alias": "Updated alias",
    "isActive": true
  }'
```

### `PUT /devs/:address/tags`

Replaces assigned tag IDs for a followed dev wallet.

```bash theme={null}
curl -X PUT 'https://api.memeperfect.io/api/external/v1/devs/DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263/tags' \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "tagIds": ["b0d6a1d8-6e6c-4f80-9a61-5b8a3c4b2a11"]
  }'
```

### `GET /notifications`

Lists notifications with pagination and optional filtering.

Query parameters:

* `page` (optional, default `1`)
* `limit` (optional, default `20`, max `100`)
* `source` (optional, default `all`; allowed: `all`, `personal`, `official`)
* `strategyId` (optional)
* `triggerType` (optional)

By default, this endpoint returns both personal alerts and official alerts from official strategies you are subscribed to. Use `source=personal` or `source=official` when you want only one alert source.

```bash theme={null}
curl -X GET 'https://api.memeperfect.io/api/external/v1/notifications?page=1&limit=20&strategyId=9a46ddf3-98fd-4cf5-8fd0-022421741c35' \
  -H 'X-API-Key: YOUR_API_KEY'
```

```json theme={null}
{
  "items": [
    {
      "id": "497e3b40-3a6e-449f-87cd-4f96af15cffe",
      "source": "personal",
      "audienceType": "personal",
      "userId": "4fcb13dc-2f0c-49bb-97a3-2f2f7ac0f80f",
      "strategyId": "9a46ddf3-98fd-4cf5-8fd0-022421741c35",
      "channel": "telegram",
      "status": "sent",
      "triggerEventType": "new_token_created",
      "triggerContext": {
        "tokenAddress": "7xKXExampleAddress",
        "tokenData": {
          "tokenSymbol": "EXAMPLE",
          "market": {
            "priceUSD": 0.00012,
            "mcap": 120000
          }
        }
      },
      "sentAt": "2026-03-09T08:31:05.120Z",
      "createdAt": "2026-03-09T08:31:05.120Z",
      "updatedAt": "2026-03-09T08:31:05.120Z",
      "strategy": {
        "id": "9a46ddf3-98fd-4cf5-8fd0-022421741c35",
        "name": "Updated E2E Strategy"
      }
    }
  ],
  "page": 1,
  "limit": 20,
  "total": 1,
  "totalPages": 1
}
```

### `GET /notifications/:id`

Returns one notification for the authenticated user, including live performance snapshot.

Query parameters:

* `source` (optional, default `all`; allowed: `all`, `personal`, `official`)
* `view` (optional, default `full`; allowed: `full`, `summary`)

```bash theme={null}
curl -X GET 'https://api.memeperfect.io/api/external/v1/notifications/497e3b40-3a6e-449f-87cd-4f96af15cffe' \
  -H 'X-API-Key: YOUR_API_KEY'
```

```json theme={null}
{
  "id": "497e3b40-3a6e-449f-87cd-4f96af15cffe",
  "source": "personal",
  "audienceType": "personal",
  "userId": "4fcb13dc-2f0c-49bb-97a3-2f2f7ac0f80f",
  "strategyId": "9a46ddf3-98fd-4cf5-8fd0-022421741c35",
  "channel": "telegram",
  "status": "sent",
  "triggerEventType": "new_token_created",
  "triggerContext": {
    "tokenAddress": "7xKXExampleAddress"
  },
  "livePerformance": {
    "priceNow": 0.00018,
    "marketCapNow": 180000,
    "peakPrice": 0.00029,
    "athPrice": 0.00029
  },
  "sentAt": "2026-03-09T08:31:05.120Z",
  "createdAt": "2026-03-09T08:31:05.120Z",
  "updatedAt": "2026-03-09T08:31:05.120Z"
}
```

### `GET /notifications/performance`

Returns performance summary for one Strategy and one range.

Required query parameters:

* `strategyId` (string)
* `range` (`daily` | `weekly` | `monthly` | `quarterly`)

```bash theme={null}
curl -X GET 'https://api.memeperfect.io/api/external/v1/notifications/performance?range=daily&strategyId=9a46ddf3-98fd-4cf5-8fd0-022421741c35' \
  -H 'X-API-Key: YOUR_API_KEY'
```

```json theme={null}
{
  "overall": {
    "totalCalls": 32,
    "winners": 9,
    "winRate": 0.28125,
    "avgAthMultiple": 1.7412,
    "bestAthMultiple": 6.2031
  },
  "buckets": [
    { "label": "<1x", "min": 0, "max": 1, "count": 10 },
    { "label": "1-1.2x", "min": 1, "max": 1.2, "count": 7 },
    { "label": "1.2-2x", "min": 1.2, "max": 2, "count": 9 },
    { "label": "2-5x", "min": 2, "max": 5, "count": 5 },
    { "label": "5-10x", "min": 5, "max": 10, "count": 1 },
    { "label": "10x+", "min": 10, "max": null, "count": 0 }
  ],
  "topTokens": [
    {
      "tokenAddress": "7xKXExampleAddress",
      "tokenSymbol": "EXAMPLE",
      "tokenName": "Example Token",
      "alertMcap": 120000,
      "currentMcap": 180000,
      "athMcap": 744372,
      "athMultiple": 6.2031
    }
  ]
}
```

### `POST /notifications/performance/jobs`

Creates an async performance job.

Required query parameters:

* `strategyId` (string)
* `range` (`daily` | `weekly` | `monthly` | `quarterly`)

```bash theme={null}
curl -X POST 'https://api.memeperfect.io/api/external/v1/notifications/performance/jobs?range=daily&strategyId=9a46ddf3-98fd-4cf5-8fd0-022421741c35' \
  -H 'X-API-Key: YOUR_API_KEY'
```

```json theme={null}
{
  "jobId": "48112"
}
```

### `GET /notifications/performance/jobs/:jobId`

Returns async performance job status and result when completed.

```bash theme={null}
curl -X GET 'https://api.memeperfect.io/api/external/v1/notifications/performance/jobs/48112' \
  -H 'X-API-Key: YOUR_API_KEY'
```

```json theme={null}
{
  "jobId": "48112",
  "status": "active",
  "rawState": "active",
  "progress": {
    "totalTokens": 200,
    "processedTokens": 58,
    "remainingTokens": 142,
    "progressPercent": 29
  }
}
```

When status is `completed`, the response includes:

* `result.overall`
* `result.buckets`
* `result.topTokens`

### `GET /webhook`

Returns webhook configuration for the authenticated user.

```bash theme={null}
curl -X GET 'https://api.memeperfect.io/api/external/v1/webhook' \
  -H 'X-API-Key: YOUR_API_KEY'
```

```json theme={null}
{
  "enabled": true,
  "url": "https://example.com/memeperfect/webhook",
  "verifiedAt": "2026-03-09T08:36:21.522Z",
  "hasSecret": true
}
```

### `PUT /webhook`

Updates webhook configuration.

Body fields:

* `enabled` (required, boolean)
* `url` (optional, string or `null`)

Behavior notes:

* Set `url` to `null` to clear webhook config and secret.
* When URL changes, backend attempts verification and may return `verificationError`.
* A new `secret` is only returned when generated or rotated.

```bash theme={null}
curl -X PUT 'https://api.memeperfect.io/api/external/v1/webhook' \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "enabled": true,
    "url": "https://example.com/memeperfect/webhook"
  }'
```

```json theme={null}
{
  "enabled": true,
  "url": "https://example.com/memeperfect/webhook",
  "verifiedAt": "2026-03-09T08:36:21.522Z",
  "hasSecret": true,
  "secret": "whsec_9SNQ...",
  "verificationError": null
}
```

## Error cases to handle

Your client should handle at least these responses:

* `400` validation errors (invalid body, query, or path params)
* `401` missing or invalid API key
* `403` no active subscription or plan limit reached
* `404` resource not found (or belongs to another user)
* `429` throttled (`Rate limit exceeded`)

<CardGroup cols={2}>
  <Card title="API Walkthrough" icon="route" href="/developers/api-walkthrough">
    Full integration flow from create to activate, observe, webhook, and
    deactivate.
  </Card>

  <Card title="Webhooks" icon="bolt" href="/developers/webhooks">
    Webhook payload format, signing, and verification flow.
  </Card>

  <Card title="Strategy Rules Spec" icon="list-checks" href="/developers/strategy-rules">
    Rule payload schema, supported ruleType IDs, operators, and value formats.
  </Card>

  <Card title="AI Agent" icon="bot" href="/developers/ai-agent">
    Build agent workflows on top of Strategy and notification outputs.
  </Card>
</CardGroup>
