{
  "openapi": "3.1.0",
  "info": {
    "title": "Crafzo IP Lookup API and content endpoints",
    "version": "1.0.0",
    "summary": "Free IP lookup site: a small JSON API, machine-readable files, and Markdown content negotiation on every page.",
    "description": "Crafzo IP Lookup is a free IP intelligence website. This document describes the endpoints an agent can call today: the JSON API under `/api/`, the machine-readable files (llms.txt, llms-full.txt, sitemap, feed) and the pages, which serve Markdown for `Accept: text/markdown`.\n\nNo authentication or API key is required. The API is offered for interactive use and light automation without a service-level guarantee; contact the publisher before building high-volume automation on it.\n\nErrors under `/api/` are JSON in the `ApiError` envelope (a stable `code`, a `message`, a `hint`, the HTTP `status` and a `docs` link). Unknown `/api/` paths return 404 in that envelope; unsupported methods return 405 with an `Allow` header.\n\nThe full IP report shown on the site (geolocation, network ownership, fraud score, VPN and proxy signals) runs inside the web application and is not exposed as a JSON endpoint. Read IP intelligence as signals, not proof.",
    "termsOfService": "https://ip.crafzo.com/terms",
    "contact": {
      "name": "Crafzo IP Lookup",
      "url": "https://ip.crafzo.com/contact",
      "email": "i@mojahidulhaque.com"
    }
  },
  "externalDocs": {
    "description": "Developer resources: API, OpenAPI and Markdown access",
    "url": "https://ip.crafzo.com/developers"
  },
  "servers": [
    {
      "url": "https://ip.crafzo.com",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "IP address",
      "description": "JSON endpoints about IP addresses."
    },
    {
      "name": "Pages",
      "description": "HTML pages that also serve Markdown. Content negotiation: the response is HTML for browsers and Markdown when the Accept header prefers `text/markdown` (or `text/plain`); q-values are honoured per RFC 9110 and every response carries `Vary: Accept`. Appending `.md` to the path returns the Markdown representation regardless of the Accept header. A request that accepts none of `text/html`, `text/markdown` or `text/plain` receives 406."
    },
    {
      "name": "Machine-readable files",
      "description": "Index, full text, sitemap, feed and this document."
    }
  ],
  "security": [],
  "paths": {
    "/api/current-ip": {
      "get": {
        "operationId": "getCurrentIp",
        "summary": "Detect the caller's public IP address",
        "description": "Returns the public IPv4 or IPv6 address of the client making the request, as the server sees it. The address is read from the connection's forwarding headers; when none carries a public address the server asks a fallback resolver, and `source` names where the answer came from. No parameters, no authentication.",
        "tags": [
          "IP address"
        ],
        "responses": {
          "200": {
            "description": "The caller's public IP address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CurrentIp"
                },
                "example": {
                  "ip": "203.0.113.42",
                  "source": "request-header"
                }
              }
            }
          },
          "502": {
            "description": "No public address could be determined from the connection or the fallback resolvers. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                },
                "example": {
                  "error": {
                    "code": "upstream_unavailable",
                    "message": "Unable to detect a public IP address.",
                    "hint": "The connection carried no public address and the fallback resolvers did not answer. Retry in a few seconds.",
                    "status": 502,
                    "docs": "https://ip.crafzo.com/developers"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "operationId": "getOpenApiDocument",
        "summary": "This OpenAPI document",
        "description": "The OpenAPI 3.1 description of the API and content endpoints, as JSON.",
        "tags": [
          "Machine-readable files"
        ],
        "responses": {
          "200": {
            "description": "This OpenAPI document",
            "content": {
              "application/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/llms.txt": {
      "get": {
        "operationId": "getLlmsIndex",
        "summary": "llms.txt index of the site",
        "description": "llmstxt.org index of every tool page and guide, with one-line descriptions.",
        "tags": [
          "Machine-readable files"
        ],
        "responses": {
          "200": {
            "description": "llms.txt index of the site",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/llms-full.txt": {
      "get": {
        "operationId": "getLlmsFullText",
        "summary": "Full text of every tool page and guide",
        "description": "Full text of every tool page and guide, with canonical URLs, dates, FAQ and sources.",
        "tags": [
          "Machine-readable files"
        ],
        "responses": {
          "200": {
            "description": "Full text of every tool page and guide",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/sitemap.xml": {
      "get": {
        "operationId": "getSitemap",
        "summary": "XML sitemap",
        "description": "XML sitemap of every indexable page (static pages, tool pages and guides) with last-modified dates.",
        "tags": [
          "Machine-readable files"
        ],
        "responses": {
          "200": {
            "description": "XML sitemap",
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/feed.xml": {
      "get": {
        "operationId": "getGuideFeed",
        "summary": "RSS feed of the guides",
        "description": "RSS 2.0 feed of the guides, newest first, with titles, descriptions and publication dates.",
        "tags": [
          "Machine-readable files"
        ],
        "responses": {
          "200": {
            "description": "RSS feed of the guides",
            "content": {
              "application/rss+xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/": {
      "get": {
        "operationId": "getHomepage",
        "summary": "Homepage: the free IP lookup tool",
        "description": "The IP lookup tool, what a report contains, the list of tools and the homepage FAQ. Open `/?ip=<address>` in a browser to run a lookup. Content negotiation: the response is HTML for browsers and Markdown when the Accept header prefers `text/markdown` (or `text/plain`); q-values are honoured per RFC 9110 and every response carries `Vary: Accept`. Appending `.md` to the path returns the Markdown representation regardless of the Accept header. A request that accepts none of `text/html`, `text/markdown` or `text/plain` receives 406.",
        "tags": [
          "Pages"
        ],
        "parameters": [
          {
            "name": "ip",
            "in": "query",
            "required": false,
            "description": "Public IPv4 or IPv6 address to pre-fill and analyze in the browser tool. The Markdown representation does not run lookups.",
            "schema": {
              "type": "string",
              "examples": [
                "8.8.8.8",
                "2001:4860:4860::8888"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The homepage as HTML, or as Markdown when `Accept: text/markdown` is preferred.",
            "headers": {
              "Vary": {
                "description": "Always includes `Accept`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "text/html": {
                "schema": {
                  "$ref": "#/components/schemas/HtmlDocument"
                }
              },
              "text/markdown": {
                "schema": {
                  "$ref": "#/components/schemas/MarkdownDocument"
                }
              }
            }
          },
          "406": {
            "$ref": "#/components/responses/NotAcceptable"
          }
        }
      }
    },
    "/blog": {
      "get": {
        "operationId": "listGuides",
        "summary": "Index of every guide",
        "description": "Every published guide with its URL, title, description and last-updated date. Content negotiation: the response is HTML for browsers and Markdown when the Accept header prefers `text/markdown` (or `text/plain`); q-values are honoured per RFC 9110 and every response carries `Vary: Accept`. Appending `.md` to the path returns the Markdown representation regardless of the Accept header. A request that accepts none of `text/html`, `text/markdown` or `text/plain` receives 406.",
        "tags": [
          "Pages"
        ],
        "responses": {
          "200": {
            "description": "The guide index as HTML, or as Markdown when `Accept: text/markdown` is preferred.",
            "headers": {
              "Vary": {
                "description": "Always includes `Accept`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "text/html": {
                "schema": {
                  "$ref": "#/components/schemas/HtmlDocument"
                }
              },
              "text/markdown": {
                "schema": {
                  "$ref": "#/components/schemas/MarkdownDocument"
                }
              }
            }
          },
          "406": {
            "$ref": "#/components/responses/NotAcceptable"
          }
        }
      }
    },
    "/blog/{slug}": {
      "get": {
        "operationId": "getGuide",
        "summary": "A guide by slug",
        "description": "One guide: title, description, dates, sections (with tables), FAQ and sources. Slugs of guides that were merged into another page answer with a permanent redirect to the page that owns the topic. Content negotiation: the response is HTML for browsers and Markdown when the Accept header prefers `text/markdown` (or `text/plain`); q-values are honoured per RFC 9110 and every response carries `Vary: Accept`. Appending `.md` to the path returns the Markdown representation regardless of the Accept header. A request that accepts none of `text/html`, `text/markdown` or `text/plain` receives 406.",
        "tags": [
          "Pages"
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Guide slug. The enum lists every published guide.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9-]+$",
              "enum": [
                "how-to-read-server-logs-and-identify-malicious-bot-ips",
                "ipv4-vs-ipv6-what-the-shift-means-for-your-privacy",
                "the-difference-between-a-vpn-a-proxy-and-a-tor-node",
                "web-scraping-without-getting-blocked-understanding-ip-trust-scores",
                "why-you-got-an-ip-banned-error-and-how-to-fix-it",
                "can-i-change-or-hide-my-ip-address",
                "how-does-an-ip-lookup-service-actually-work",
                "can-you-find-someone-s-address-from-their-ip",
                "best-free-ip-lookup-tools-which-one-should-you-use",
                "how-accurate-are-ip-address-location-lookups",
                "how-do-i-check-my-own-ip-address",
                "is-it-illegal-to-look-up-someone-s-ip-address",
                "what-information-can-people-see-from-your-ip-address",
                "can-someone-find-my-location-from-my-ip-address",
                "how-to-find-someone-s-ip-address-a-simple-guide",
                "what-is-an-ip-address-and-why-should-i-care",
                "credential-stuffing-ip-intelligence",
                "ip-risk-score-for-fraud-teams",
                "ip-risk-score-for-ecommerce",
                "residential-proxy-detection-for-login-risk",
                "ip-intelligence-for-login-risk",
                "ip-geo-location-for-illegal-iptv",
                "ip-geolocation-service",
                "starlink-ip-addresses-geolocation",
                "what-does-it-mean-when-your-ip-is-blacklisted",
                "ip-geolocation-api-comparison",
                "ip-location-api-pricing",
                "track-public-geolocation",
                "ip-lookup",
                "ip-location-lookup-guide",
                "isp-lookup-from-ip",
                "public-vs-private-ip",
                "reduce-fake-signups-with-ip-risk",
                "understand-ip-health-analysis",
                "troubleshoot-wrong-ip-address",
                "how-to-hide-ip-address",
                "why-does-ip-address-change",
                "ip-fraud-score-explained",
                "how-to-check-ip-blacklist",
                "how-to-tell-if-ip-is-vpn",
                "ip-lookup-wrong-location-reasons",
                "get-user-ip-nextjs-app-router",
                "x-forwarded-for-real-client-ip",
                "signs-ip-flagged-suspicious",
                "free-vs-paid-ip-lookup-tools",
                "rate-limiting-ip-nodejs-guide",
                "asn-lookup-explained",
                "ip-whois-rdap-lookup",
                "find-abuse-contact-from-ip",
                "cgnat-shared-ip-addresses",
                "check-vpn-working-ip-lookup",
                "tor-exit-node-ip-risk",
                "data-center-ip-vs-residential-ip",
                "cloudflare-proxy-ip-addresses",
                "reverse-dns-lookup-ip-reputation",
                "traceroute-vs-ip-geolocation",
                "subnet-cidr-ip-range-basics",
                "private-reserved-special-ip-addresses",
                "mobile-carrier-ip-geolocation",
                "impossible-travel-detection-ip",
                "api-rate-limiting-by-ip",
                "webhook-allowlist-ip-security",
                "firewall-block-ip-best-practices",
                "privacy-safe-ip-logging",
                "incident-response-ip-triage"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The guide as HTML, or as Markdown when `Accept: text/markdown` is preferred.",
            "headers": {
              "Vary": {
                "description": "Always includes `Accept`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "text/html": {
                "schema": {
                  "$ref": "#/components/schemas/HtmlDocument"
                }
              },
              "text/markdown": {
                "schema": {
                  "$ref": "#/components/schemas/MarkdownDocument"
                }
              }
            }
          },
          "308": {
            "description": "The slug belonged to a guide that was merged into another page; `Location` is the page that owns the topic.",
            "headers": {
              "Location": {
                "description": "Absolute URL of the destination page.",
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          },
          "404": {
            "description": "No guide has this slug.",
            "content": {
              "text/html": {
                "schema": {
                  "$ref": "#/components/schemas/HtmlDocument"
                }
              },
              "text/markdown": {
                "schema": {
                  "$ref": "#/components/schemas/MarkdownDocument"
                }
              }
            }
          },
          "406": {
            "$ref": "#/components/responses/NotAcceptable"
          }
        }
      }
    },
    "/{toolSlug}": {
      "get": {
        "operationId": "getToolPage",
        "summary": "A free IP tool page by slug",
        "description": "One tool page: what the tool checks, how to use it, common uses and FAQ. The tools run in the browser; the Markdown representation describes them. Content negotiation: the response is HTML for browsers and Markdown when the Accept header prefers `text/markdown` (or `text/plain`); q-values are honoured per RFC 9110 and every response carries `Vary: Accept`. Appending `.md` to the path returns the Markdown representation regardless of the Accept header. A request that accepts none of `text/html`, `text/markdown` or `text/plain` receives 406.",
        "tags": [
          "Pages"
        ],
        "parameters": [
          {
            "name": "toolSlug",
            "in": "path",
            "required": true,
            "description": "Tool page slug. The enum lists every tool page.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9-]+$",
              "enum": [
                "what-is-my-ip",
                "ip-address-lookup-tool",
                "ip-location-lookup",
                "ipv6-lookup",
                "free-ip-checker",
                "ip-fraud-score-checker",
                "ip-reputation-check",
                "vpn-proxy-checker",
                "botnet-ip-lookup",
                "asn-lookup"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The tool page as HTML, or as Markdown when `Accept: text/markdown` is preferred.",
            "headers": {
              "Vary": {
                "description": "Always includes `Accept`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "text/html": {
                "schema": {
                  "$ref": "#/components/schemas/HtmlDocument"
                }
              },
              "text/markdown": {
                "schema": {
                  "$ref": "#/components/schemas/MarkdownDocument"
                }
              }
            }
          },
          "404": {
            "description": "No tool page has this slug.",
            "content": {
              "text/html": {
                "schema": {
                  "$ref": "#/components/schemas/HtmlDocument"
                }
              },
              "text/markdown": {
                "schema": {
                  "$ref": "#/components/schemas/MarkdownDocument"
                }
              }
            }
          },
          "406": {
            "$ref": "#/components/responses/NotAcceptable"
          }
        }
      }
    },
    "/developers": {
      "get": {
        "operationId": "getDeveloperDocs",
        "summary": "Developer resources",
        "description": "How to use the API, the machine-readable files and Markdown negotiation, with example requests and the error format. Content negotiation: the response is HTML for browsers and Markdown when the Accept header prefers `text/markdown` (or `text/plain`); q-values are honoured per RFC 9110 and every response carries `Vary: Accept`. Appending `.md` to the path returns the Markdown representation regardless of the Accept header. A request that accepts none of `text/html`, `text/markdown` or `text/plain` receives 406.",
        "tags": [
          "Pages"
        ],
        "responses": {
          "200": {
            "description": "The developer resources page as HTML, or as Markdown when `Accept: text/markdown` is preferred.",
            "headers": {
              "Vary": {
                "description": "Always includes `Accept`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "text/html": {
                "schema": {
                  "$ref": "#/components/schemas/HtmlDocument"
                }
              },
              "text/markdown": {
                "schema": {
                  "$ref": "#/components/schemas/MarkdownDocument"
                }
              }
            }
          },
          "406": {
            "$ref": "#/components/responses/NotAcceptable"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "CurrentIp": {
        "type": "object",
        "description": "The caller's public IP address and where the server read it from.",
        "required": [
          "ip",
          "source"
        ],
        "properties": {
          "ip": {
            "type": "string",
            "description": "Public IPv4 or IPv6 address of the client.",
            "examples": [
              "203.0.113.42",
              "2001:db8::1"
            ]
          },
          "source": {
            "type": "string",
            "description": "`request-header` when the address was read from the connection's forwarding headers; otherwise the hostname of the fallback resolver that answered.",
            "examples": [
              "request-header"
            ]
          }
        },
        "additionalProperties": false
      },
      "ApiError": {
        "type": "object",
        "description": "Error envelope returned by every `/api/` endpoint.",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "hint",
              "status",
              "docs"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine-readable error code.",
                "enum": [
                  "not_found",
                  "method_not_allowed",
                  "upstream_unavailable"
                ]
              },
              "message": {
                "type": "string",
                "description": "What went wrong, in plain language."
              },
              "hint": {
                "type": "string",
                "description": "How to resolve or work around the error."
              },
              "status": {
                "type": "integer",
                "description": "The HTTP status of the response.",
                "minimum": 400,
                "maximum": 599
              },
              "docs": {
                "type": "string",
                "format": "uri",
                "description": "Where the API is documented."
              }
            },
            "additionalProperties": false
          }
        },
        "additionalProperties": false
      },
      "HtmlDocument": {
        "type": "string",
        "description": "The page as HTML."
      },
      "MarkdownDocument": {
        "type": "string",
        "description": "The page as Markdown: a level-one title, a `Canonical:` line with the page URL, the page's sections, and a closing list of links to the HTML version, llms.txt and the developer resources."
      }
    },
    "responses": {
      "NotAcceptable": {
        "description": "The Accept header accepts none of `text/html`, `text/markdown` or `text/plain`.",
        "headers": {
          "Vary": {
            "description": "Always `Accept`.",
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "text/plain": {
            "schema": {
              "type": "string"
            },
            "example": "Not Acceptable\n\nAvailable: text/html, text/markdown, text/plain\n"
          }
        }
      }
    }
  }
}