{
  "openapi": "3.1.0",
  "info": {
    "title": "IPASK API",
    "version": "1.0.0",
    "description": "Residential IP verification: classify an address, batch-check a pool, and read evidence fields. Single lookups are free (120 req/min per IP); batch uses a license key.",
    "contact": {
      "url": "https://ipask.net/contact/"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://ipask.net",
      "description": "Production"
    }
  ],
  "security": [],
  "tags": [
    {
      "name": "lookup",
      "description": "Single and batch IP checks"
    },
    {
      "name": "watch",
      "description": "Pool monitoring with optional webhook"
    },
    {
      "name": "account",
      "description": "Trial, keys and quotas"
    },
    {
      "name": "system",
      "description": "Health and status"
    },
    {
      "name": "privacy",
      "description": "Data erasure"
    }
  ],
  "paths": {
    "/api/v1/health": {
      "get": {
        "tags": [
          "system"
        ],
        "summary": "Health and data freshness",
        "operationId": "health",
        "responses": {
          "200": {
            "description": "Service health",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/me": {
      "get": {
        "tags": [
          "lookup"
        ],
        "summary": "Look up the caller IP",
        "operationId": "me",
        "responses": {
          "200": {
            "description": "Lookup of the requesting IP",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Lookup"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/lookup": {
      "get": {
        "tags": [
          "lookup"
        ],
        "summary": "Look up one IP",
        "operationId": "lookup",
        "parameters": [
          {
            "name": "ip",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "IPv4 or IPv6. Omit for the caller IP."
          }
        ],
        "responses": {
          "200": {
            "description": "Verdict and evidence",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Lookup"
                }
              }
            }
          },
          "400": {
            "description": "invalid_ip",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/lookup/{ip}": {
      "get": {
        "tags": [
          "lookup"
        ],
        "summary": "Look up one IP (path style)",
        "operationId": "lookupByPath",
        "parameters": [
          {
            "name": "ip",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Verdict and evidence",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Lookup"
                }
              }
            }
          },
          "400": {
            "description": "invalid_ip",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/batch": {
      "get": {
        "tags": [
          "lookup"
        ],
        "summary": "Batch check (comma-separated)",
        "operationId": "batchGet",
        "parameters": [
          {
            "name": "ips",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated IPs"
          },
          {
            "name": "key",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Batch results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Batch"
                }
              }
            }
          },
          "400": {
            "description": "missing_ips",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "bad_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "batch_too_large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "quota_exhausted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "lookup"
        ],
        "summary": "Batch check (JSON body)",
        "operationId": "batchPost",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ips": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "ips"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Batch results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Batch"
                }
              }
            }
          },
          "400": {
            "description": "invalid_json",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "bad_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "batch_too_large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/watch": {
      "get": {
        "tags": [
          "watch"
        ],
        "summary": "Read a monitored pool",
        "operationId": "watchGet",
        "parameters": [
          {
            "name": "key",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "keyQuery": []
          }
        ],
        "responses": {
          "200": {
            "description": "Pool status and change history",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Watch"
                }
              }
            }
          },
          "401": {
            "description": "bad_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "watch"
        ],
        "summary": "Save a pool and optional webhook",
        "operationId": "watchPost",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "keyQuery": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "key": {
                    "type": "string"
                  },
                  "ips": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "webhook": {
                    "type": "string",
                    "description": "http(s) URL; POSTed when the daily check finds changes"
                  }
                },
                "required": [
                  "key",
                  "ips"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WatchSave"
                }
              }
            }
          },
          "400": {
            "description": "bad_webhook",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "bad_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/watch/check": {
      "post": {
        "tags": [
          "watch"
        ],
        "summary": "Run the pool check now",
        "operationId": "watchCheck",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "keyQuery": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "key": {
                    "type": "string"
                  }
                },
                "required": [
                  "key"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Check result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WatchCheck"
                }
              }
            }
          },
          "400": {
            "description": "empty_pool",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "bad_key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/trial": {
      "post": {
        "tags": [
          "account"
        ],
        "summary": "Claim a 3-day trial key (one per IP per 30 days)",
        "operationId": "trial",
        "responses": {
          "200": {
            "description": "Trial key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Key"
                }
              }
            }
          },
          "429": {
            "description": "cooldown",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/erase": {
      "post": {
        "tags": [
          "privacy"
        ],
        "summary": "Erase all records for one IP (10/day per caller IP)",
        "operationId": "erase",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ip": {
                    "type": "string"
                  },
                  "confirm": {
                    "type": "string",
                    "enum": [
                      "erase"
                    ]
                  }
                },
                "required": [
                  "ip",
                  "confirm"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Erased",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Erase"
                }
              }
            }
          },
          "400": {
            "description": "invalid_ip",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "erase_daily_limit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/contact": {
      "post": {
        "tags": [
          "system"
        ],
        "summary": "Contact form (3/hour per IP)",
        "operationId": "contact",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string",
                    "maxLength": 4000
                  }
                },
                "required": [
                  "message"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "description": "message_too_short",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/order/create": {
      "get": {
        "tags": [
          "account"
        ],
        "summary": "Create a checkout order",
        "operationId": "orderCreate",
        "parameters": [
          {
            "name": "plan",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "pro",
                "studio"
              ]
            }
          },
          {
            "name": "email",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Payment URL",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderCreate"
                }
              }
            }
          },
          "502": {
            "description": "pay_failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/order/{id}": {
      "get": {
        "tags": [
          "account"
        ],
        "summary": "Read an order (also reconciles payment status)",
        "operationId": "orderGet",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Order",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "License key (ipk_…)"
      },
      "keyQuery": {
        "type": "apiKey",
        "in": "query",
        "name": "key"
      }
    },
    "schemas": {
      "Ok": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "Health": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok"
            ]
          },
          "uptime_seconds": {
            "type": "number"
          },
          "data": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "required": [
          "status"
        ]
      },
      "Lookup": {
        "type": "object",
        "properties": {
          "ip": {
            "type": "string"
          },
          "version": {
            "type": [
              "integer",
              "null"
            ],
            "enum": [
              4,
              6,
              null
            ]
          },
          "found": {
            "type": "boolean"
          },
          "country": {
            "type": [
              "string",
              "null"
            ]
          },
          "country_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "network": {
            "type": "object",
            "properties": {
              "asn": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "asn_name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "asn_type": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "hosting | isp | cdn | education | government | business | null"
              },
              "cloud_provider": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "vpn_provider": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "prefix": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "security": {
            "type": "object",
            "properties": {
              "is_datacenter": {
                "type": "boolean"
              },
              "is_vpn": {
                "type": "boolean"
              },
              "is_tor": {
                "type": "boolean"
              },
              "is_proxy": {
                "type": "boolean"
              },
              "is_cdn": {
                "type": "boolean"
              },
              "is_drop": {
                "type": "boolean"
              },
              "risk_score": {
                "type": "integer",
                "minimum": 0,
                "maximum": 100
              },
              "risk_level": {
                "type": "string",
                "enum": [
                  "low",
                  "medium",
                  "high"
                ]
              }
            }
          },
          "meta": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "required": [
          "ip",
          "found",
          "network",
          "security"
        ]
      },
      "Batch": {
        "type": "object",
        "properties": {
          "count": {
            "type": "integer"
          },
          "trimmed": {
            "type": "integer"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Lookup"
            }
          },
          "quota": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "Watch": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "ips": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "last": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "changes": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "checked": {
            "type": [
              "string",
              "null"
            ]
          },
          "webhook": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "WatchSave": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "count": {
            "type": "integer"
          },
          "webhook": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "WatchCheck": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "changes": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "checked": {
            "type": "integer"
          }
        }
      },
      "Key": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string"
          },
          "expires_at": {
            "type": "string"
          },
          "daily": {
            "type": "integer"
          },
          "batch": {
            "type": "integer"
          },
          "days": {
            "type": "integer"
          }
        }
      },
      "Erase": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "ip": {
            "type": "string"
          },
          "total": {
            "type": "integer"
          },
          "counts": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          }
        }
      },
      "OrderCreate": {
        "type": "object",
        "properties": {
          "orderId": {
            "type": "string"
          },
          "paymentUrl": {
            "type": "string"
          },
          "amountCny": {
            "type": "number"
          },
          "actualAmount": {
            "type": "number"
          }
        }
      },
      "Order": {
        "type": "object",
        "properties": {
          "order": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "plan": {
                "type": "string"
              },
              "email": {
                "type": "string"
              },
              "amountCny": {
                "type": "number"
              },
              "status": {
                "type": "string",
                "enum": [
                  "pending",
                  "paid",
                  "expired",
                  "cancelled"
                ]
              },
              "key": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "createdAt": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  }
}
