ipscanner

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 9, 2026 License: MIT Imports: 14 Imported by: 0

README

ipscanner-go

The official Go client for the IPScanner API.

Full API reference: https://ipscanner.io/api-documentation

Install

go get github.com/ipscanner/ipscanner-go

Requires Go 1.23 or later. No dependencies outside the standard library.

Quick start

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/ipscanner/ipscanner-go"
)

func main() {
	client := ipscanner.NewClient(ipscanner.WithAPIKey("pk_live_..."))

	res, err := client.IP.Lookup(context.Background(), "1.1.1.1")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(res.NetworkClass, res.Purity.Grade, res.Verdict.Confidence)
}

Usage

Every method takes a context.Context first.

IP
res, err := client.IP.Lookup(ctx, "8.8.8.8")    // IPv4, IPv6, CIDR or hostname
vpn, err := client.IP.VPN(ctx, "2001:db8::1")
proxy, err := client.IP.Proxy(ctx, "1.2.3.4")
geo, err := client.IP.Geo(ctx, "1.2.3.4")
asn, err := client.IP.ASN(ctx, "1.2.3.4")
whois, err := client.IP.Whois(ctx, "example.com")
hist, err := client.IP.History(ctx, &ipscanner.HistoryParams{Limit: 50})
demo, err := client.IP.Demo(ctx, "1.2.3.4") // no key required
me, err := client.IP.MyIP(ctx)              // no key required

VPNProvider is the VPN brand (for example Mullvad) when known; Provider is the network owner.

On the Free plan premium fields (purity, provider, VPN brand, precise location) come back as JSON null, so they decode as zero values, and are listed in Locked with PlanRequired.

Bulk
out, err := client.Bulk.Check(ctx, ipscanner.BulkParams{IPs: []string{"1.1.1.1", "8.8.8.8"}})

for ev, err := range client.Bulk.Stream(ctx, ipscanner.BulkParams{Input: pasted}) {
	if err != nil {
		log.Fatal(err)
	}
	switch ev.Type {
	case "result":
		fmt.Println(ev.Index, ev.IP, ev.Grade)
	case "error":
		fmt.Println(ev.Index, ev.Input, ev.Reason)
	case "done":
		fmt.Println("complete:", ev.Complete())
	}
}

Stream reads the NDJSON response line by line. It uses a 5 minute timeout unless the context already has a deadline. Complete() is false when the run stopped early (quota or server deadline).

Agentscan
v, err := client.Agentscan.Check(ctx, ipscanner.AgentscanCheckParams{
	IP:        "1.2.3.4",
	UserAgent: r.UserAgent(),
	Headers:   map[string]string{"accept-language": "en"},
})

batch, err := client.Agentscan.Batch(ctx, []ipscanner.AgentscanBatchLine{{Line: 1, IP: "1.2.3.4", UserAgent: "GPTBot/1.0"}})
check, err := client.Agentscan.Verify(ctx, ipscanner.AgentscanVerifyParams{IP: "66.249.66.1", UserAgent: "Googlebot"})
agents, err := client.Agentscan.Allowlist(ctx)
self, err := client.Agentscan.SelfCheck(ctx, nil)
Edge and sites
res, err := client.Edge.Check(ctx, ipscanner.EdgeCheckParams{
	IP:        "1.2.3.4",
	Site:      "site_...",
	UserAgent: r.UserAgent(),
})
fmt.Println(res.Class, res.Site != nil)

policy, err := client.Sites.Policy(ctx, "site_...")

Edge.Check is metered as 2 requests. Sites.Policy is not metered.

Gate

Runs on your server. Sends no API key; the site secret is the credential. Never retried.

v, err := client.Gate.Verify(ctx, ipscanner.GateVerifyParams{
	Secret:   "gs_...",
	Token:    token, // the token gate.js added to the form
	RemoteIP: clientIP,
})
if err == nil && v.Action != "block" {
	// accept the form
}
Provenance
att, err := client.Provenance.Check(ctx, ipscanner.ProvenanceCheckParams{IP: "1.2.3.4", ClaimedJurisdiction: "uk"})
ok, err := client.Provenance.Verify(ctx)
anchored, err := client.Provenance.VerifyAnchored(ctx)
page, err := client.Provenance.Chain(ctx, &ipscanner.ChainParams{Limit: 100})
list, err := client.Provenance.Jurisdictions(ctx)
csv, err := client.Provenance.Export(ctx, &ipscanner.ExportParams{From: "2026-10-01", To: "2026-10-31"})
Account
limits, err := client.Account.Limits(ctx)
usage, err := client.Account.Usage(ctx)
ASN directory

No key required.

top, err := client.ASNDirectory.Top(ctx, &ipscanner.ASNTopParams{Top: 20, By: "addresses"})
found, err := client.ASNDirectory.Search(ctx, "cloudflare", nil)
detail, err := client.ASNDirectory.Get(ctx, "AS15169")
Crawlers

No key required.

list, err := client.Crawlers.List(ctx)

Errors

Non-2xx responses return *ipscanner.APIError with Status, Code, Message, Details and the raw Body. A 429 returns *ipscanner.RateLimitError, which also matches *APIError with errors.As.

res, err := client.IP.Lookup(ctx, "1.1.1.1")

var rl *ipscanner.RateLimitError
var apiErr *ipscanner.APIError
switch {
case errors.As(err, &rl):
	fmt.Println(rl.Reason, rl.RetryAfter, rl.Remaining, rl.UpgradeURL)
case ipscanner.IsAuth(err):
	// 401 or 403
case ipscanner.IsNotFound(err):
	// 404
case errors.As(err, &apiErr):
	fmt.Println(apiErr.Status, apiErr.Code, apiErr.Message)
}

Network failures and timeouts return *ipscanner.ConnectionError. GET requests are retried up to 2 times on network errors and 502, 503 or 504 responses. POST requests and 429 responses are never retried.

Configuration

client := ipscanner.NewClient(
	ipscanner.WithAPIKey("pk_live_..."),           // default: IPSCANNER_API_KEY
	ipscanner.WithBaseURL("https://ipscanner.io"), // default: IPSCANNER_API_URL or https://ipscanner.io
	ipscanner.WithTimeout(30*time.Second),         // per request, when ctx has no deadline
	ipscanner.WithMaxRetries(2),
	ipscanner.WithHTTPClient(&http.Client{}),
	ipscanner.WithUserAgent("my-app/1.0"),
)

Licence

MIT

Documentation

Overview

Package ipscanner is the official Go client for the IPScanner API.

See https://ipscanner.io/api-documentation for the full API reference.

Index

Examples

Constants

View Source
const DefaultBaseURL = "https://ipscanner.io"

DefaultBaseURL is the API host used when no base URL is configured.

View Source
const Version = "0.2.0"

Version is the SDK version sent in the User-Agent header.

Variables

This section is empty.

Functions

func IsAuth

func IsAuth(err error) bool

IsAuth reports whether err is a 401 or 403 API error.

func IsNotFound

func IsNotFound(err error) bool

IsNotFound reports whether err is a 404 API error.

func IsRateLimited

func IsRateLimited(err error) bool

IsRateLimited reports whether err is a 429 API error.

Types

type APIError

type APIError struct {
	Status  int
	Code    string
	Message string
	Details map[string]any
	Body    []byte
}

APIError is returned when the API responds with a non-2xx status.

func (*APIError) Error

func (e *APIError) Error() string

func (*APIError) Invalid

func (e *APIError) Invalid() []string

Invalid returns details.invalid from a bulk 400 response.

type ASNDetail

type ASNDetail struct {
	ASNSummary
	SamplePrefixes          []ASNPrefix `json:"samplePrefixes"`
	VPNRangePrefixes        int         `json:"vpnRangePrefixes"`
	DatacenterRangePrefixes int         `json:"datacenterRangePrefixes"`
	RangesLoaded            bool        `json:"rangesLoaded"`
}

ASNDetail is an autonomous system with sample prefixes and range counts.

type ASNDirectoryService

type ASNDirectoryService struct {
	// contains filtered or unexported fields
}

ASNDirectoryService browses the public autonomous system directory. No API key required.

func (*ASNDirectoryService) Get

Get returns one autonomous system. asn may be "15169" or "AS15169".

func (*ASNDirectoryService) Search

func (s *ASNDirectoryService) Search(ctx context.Context, query string, params *ASNSearchParams) (*ASNSearchResult, error)

Search finds autonomous systems by name or number. params may be nil.

func (*ASNDirectoryService) Top

Top lists the largest autonomous systems. params may be nil.

type ASNInfo

type ASNInfo struct {
	IP      string `json:"ip"`
	ASN     string `json:"asn"`
	Name    string `json:"name"`
	Type    string `json:"type"`
	Country string `json:"country"`
}

ASNInfo is the response of GET /v1/asn/{ip}.

type ASNPrefix

type ASNPrefix struct {
	Prefix    string `json:"prefix"`
	Version   int    `json:"version"`
	Addresses uint64 `json:"addresses,omitempty"`
}

ASNPrefix is one announced prefix.

type ASNSearchParams

type ASNSearchParams struct {
	Limit int
}

ASNSearchParams controls ASNDirectory.Search.

type ASNSearchResult

type ASNSearchResult struct {
	Total   int          `json:"total"`
	Query   string       `json:"query"`
	Count   int          `json:"count"`
	Results []ASNSummary `json:"results"`
}

ASNSearchResult lists autonomous systems matching a query.

type ASNSummary

type ASNSummary struct {
	ASN          uint32 `json:"asn"`
	Label        string `json:"label"`
	Org          string `json:"org"`
	NetworkType  string `json:"networkType"`
	IPv4Prefixes int    `json:"ipv4Prefixes"`
	IPv6Prefixes int    `json:"ipv6Prefixes"`
	Prefixes     int    `json:"prefixes"`
	Addresses    uint64 `json:"addresses"`
}

ASNSummary is one autonomous system in the directory.

type ASNTopParams

type ASNTopParams struct {
	Top int
	By  string
}

ASNTopParams controls ASNDirectory.Top. By is "addresses" or "prefixes".

type ASNTopResult

type ASNTopResult struct {
	Total   int          `json:"total"`
	By      string       `json:"by"`
	Count   int          `json:"count"`
	Results []ASNSummary `json:"results"`
}

ASNTopResult lists the largest autonomous systems.

type AccountService

type AccountService struct {
	// contains filtered or unexported fields
}

AccountService reports plan limits and usage.

func (*AccountService) Limits

func (s *AccountService) Limits(ctx context.Context) (*Limits, error)

Limits returns the account's plan limits.

func (*AccountService) Usage

func (s *AccountService) Usage(ctx context.Context) (*UsageSummary, error)

Usage returns the account's usage summary.

type AgentscanBatchLine

type AgentscanBatchLine struct {
	Line      int               `json:"line"`
	IP        string            `json:"ip"`
	UserAgent string            `json:"user_agent,omitempty"`
	JA4       string            `json:"ja4,omitempty"`
	Headers   map[string]string `json:"headers,omitempty"`
}

AgentscanBatchLine is one access-log line for Agentscan.Batch.

type AgentscanBatchOutcome

type AgentscanBatchOutcome struct {
	Line       int            `json:"line"`
	IP         string         `json:"ip"`
	UserAgent  string         `json:"userAgent,omitempty"`
	Class      string         `json:"class,omitempty"`
	Confidence float64        `json:"confidence,omitempty"`
	Action     string         `json:"action,omitempty"`
	Signals    map[string]any `json:"signals,omitempty"`
	Error      string         `json:"error,omitempty"`
}

AgentscanBatchOutcome is the result for one batch line. Error is set when the line failed.

type AgentscanBatchResult

type AgentscanBatchResult struct {
	Submitted int                     `json:"submitted"`
	Processed int                     `json:"processed"`
	Failed    int                     `json:"failed"`
	Truncated bool                    `json:"truncated"`
	Reason    string                  `json:"reason"`
	ByClass   map[string]int          `json:"byClass"`
	Results   []AgentscanBatchOutcome `json:"results"`
	AsOf      time.Time               `json:"asOf"`
}

AgentscanBatchResult is the response of Agentscan.Batch.

type AgentscanCheckParams

type AgentscanCheckParams struct {
	IP            string            `json:"ip"`
	UserAgent     string            `json:"user_agent,omitempty"`
	JA4           string            `json:"ja4,omitempty"`
	Headers       map[string]string `json:"headers,omitempty"`
	HeadlessFlags map[string]bool   `json:"headless_flags,omitempty"`
	RequestID     string            `json:"request_id,omitempty"`
}

AgentscanCheckParams is the input for Agentscan.Check.

type AgentscanSelfCheckParams

type AgentscanSelfCheckParams struct {
	IP        string            `json:"ip,omitempty"`
	UserAgent string            `json:"user_agent,omitempty"`
	Headers   map[string]string `json:"headers,omitempty"`
	JA4       string            `json:"ja4,omitempty"`
}

AgentscanSelfCheckParams is the optional input for Agentscan.SelfCheck.

type AgentscanSelfCheckResult

type AgentscanSelfCheckResult struct {
	IP           string         `json:"ip"`
	UserAgent    string         `json:"userAgent"`
	JA4          string         `json:"ja4"`
	JA4Source    string         `json:"ja4Source"`
	Class        string         `json:"class"`
	Confidence   float64        `json:"confidence"`
	Action       string         `json:"action"`
	Signals      map[string]any `json:"signals"`
	Metered      bool           `json:"metered"`
	AsOf         time.Time      `json:"asOf"`
	ASN          *ASNInfo       `json:"asn,omitempty"`
	Geo          *Geolocation   `json:"geo,omitempty"`
	Locked       []string       `json:"locked,omitempty"`
	PlanRequired string         `json:"planRequired,omitempty"`
}

AgentscanSelfCheckResult is the classification of the calling connection.

type AgentscanService

type AgentscanService struct {
	// contains filtered or unexported fields
}

AgentscanService classifies requests as human, known bot, AI agent or malicious automation.

func (*AgentscanService) Allowlist

func (s *AgentscanService) Allowlist(ctx context.Context) ([]AllowlistAgent, error)

Allowlist returns the allowlist entries in effect for the account.

func (*AgentscanService) Batch

Batch classifies up to 25,000 parsed access-log lines.

func (*AgentscanService) Check

Check classifies one request.

func (*AgentscanService) SelfCheck

SelfCheck classifies the calling connection. params may be nil. Not metered.

func (*AgentscanService) Verify

Verify checks whether an address really belongs to the crawler it claims to be.

type AgentscanVerdict

type AgentscanVerdict struct {
	Class      string         `json:"class"`
	Confidence float64        `json:"confidence"`
	Action     string         `json:"action"`
	Signals    map[string]any `json:"signals"`
}

AgentscanVerdict is the classification of one request.

type AgentscanVerifyParams

type AgentscanVerifyParams struct {
	IP        string `json:"ip"`
	Bot       string `json:"bot,omitempty"`
	UserAgent string `json:"user_agent,omitempty"`
}

AgentscanVerifyParams is the input for Agentscan.Verify. Set Bot or UserAgent.

type AllowlistAgent

type AllowlistAgent struct {
	ID                 int64     `json:"id"`
	Ident              string    `json:"ident"`
	Type               string    `json:"type"`
	VerificationMethod string    `json:"verificationMethod"`
	CustomerID         *int      `json:"customerId"`
	CreatedAt          time.Time `json:"createdAt"`
}

AllowlistAgent is an allowlisted bot or crawler. CustomerID is nil for global entries.

type Anchor

type Anchor struct {
	Day      string `json:"day"`
	RootHash string `json:"rootHash"`
	LastID   int64  `json:"lastId"`
}

Anchor is a daily root hash sealing the chain up to LastID.

type AnchoredVerification

type AnchoredVerification struct {
	OK       bool      `json:"ok"`
	Count    int64     `json:"count"`
	BrokenAt int64     `json:"brokenAt"`
	Yours    int64     `json:"yours"`
	Anchor   *Anchor   `json:"anchor"`
	At       time.Time `json:"at"`
}

AnchoredVerification is a fresh chain verification with the latest daily anchor.

type BulkCheckResult

type BulkCheckResult struct {
	Submitted    int            `json:"submitted"`
	Unique       int            `json:"unique"`
	Duplicates   int            `json:"duplicates"`
	Invalid      []string       `json:"invalid"`
	Summary      map[string]int `json:"summary"`
	Results      []BulkResult   `json:"results"`
	PlanRequired string         `json:"planRequired,omitempty"`
}

BulkCheckResult is the response of POST /v1/bulk/check.

type BulkEvent

type BulkEvent struct {
	Type           string   `json:"type"`
	Index          int      `json:"index"`
	Total          int      `json:"total"`
	Input          string   `json:"input"`
	Reason         string   `json:"reason"`
	Message        string   `json:"message"`
	Error          string   `json:"error"`
	IP             string   `json:"ip"`
	Verdict        string   `json:"verdict"`
	Classification string   `json:"classification"`
	Confidence     float64  `json:"confidence"`
	Anonymized     bool     `json:"anonymized"`
	VPNProvider    string   `json:"vpnProvider"`
	Score          int      `json:"score"`
	Grade          string   `json:"grade"`
	IsTorExit      bool     `json:"isTorExit"`
	ASN            string   `json:"asn"`
	ASNName        string   `json:"asnName"`
	ASNType        string   `json:"asnType"`
	Country        string   `json:"country"`
	Processed      int      `json:"processed"`
	Failed         int      `json:"failed"`
	Metered        int      `json:"metered"`
	PlanRequired   string   `json:"planRequired"`
	Locked         []string `json:"locked"`
}

BulkEvent is one line of the Bulk.Stream response. Type is meta, result, error or done.

func (*BulkEvent) Complete

func (e *BulkEvent) Complete() bool

Complete reports whether a done event covers every address in the run.

type BulkParams

type BulkParams struct {
	IPs   []string `json:"ips,omitempty"`
	Input string   `json:"input,omitempty"`
}

BulkParams is the input for Bulk.Check and Bulk.Stream. Set IPs, Input, or both.

type BulkResult

type BulkResult struct {
	Input             string      `json:"input"`
	IP                string      `json:"ip"`
	Port              int         `json:"port,omitempty"`
	Score             int         `json:"score"`
	Grade             string      `json:"grade"`
	Verdict           string      `json:"verdict"`
	Classification    string      `json:"classification"`
	Confidence        float64     `json:"confidence"`
	Anonymized        bool        `json:"anonymized"`
	VPNProvider       string      `json:"vpnProvider,omitempty"`
	IsTorExit         bool        `json:"isTorExit"`
	InVPNRange        bool        `json:"inVpnRange"`
	InDatacenterRange bool        `json:"inDatacenterRange"`
	ASN               string      `json:"asn,omitempty"`
	ASNName           string      `json:"asnName,omitempty"`
	ASNType           string      `json:"asnType,omitempty"`
	Country           string      `json:"country,omitempty"`
	Deductions        []Deduction `json:"deductions"`
	Locked            []string    `json:"locked,omitempty"`
}

BulkResult is one graded address in a bulk check.

type BulkService

type BulkService struct {
	// contains filtered or unexported fields
}

BulkService covers batch IP checks.

func (*BulkService) Check

func (s *BulkService) Check(ctx context.Context, params BulkParams) (*BulkCheckResult, error)

Check grades up to the plan's bulk cap of addresses in one response.

func (*BulkService) Stream

func (s *BulkService) Stream(ctx context.Context, params BulkParams) iter.Seq2[*BulkEvent, error]

Stream grades addresses and yields one event per NDJSON line as it arrives. A 5 minute timeout applies unless ctx already has a deadline.

Example
package main

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"

	"github.com/ipscanner/ipscanner-go"
)

func fakeAPI() *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch r.URL.Path {
		case "/v1/ip/lookup":
			io.WriteString(w, `{"target":{"raw":"1.1.1.1","kind":"ipv4","ip":"1.1.1.1"},"networkClass":"hosting",
				"purity":{"score":70,"grade":"B","verdict":"suspect"},"at":"2026-10-06T12:00:00Z"}`)
		case "/v1/ip/bulk":
			io.WriteString(w, `{"type":"meta","total":2}
{"type":"result","ip":"1.1.1.1","grade":"B"}
{"type":"result","index":1,"ip":"8.8.8.8","grade":"A"}
{"type":"done","reason":"complete","total":2,"processed":2}
`)
		case "/v1/user/limits":
			w.WriteHeader(http.StatusTooManyRequests)
			io.WriteString(w, `{"error":"rate_limit_exceeded","reason":"monthly_quota","message":"Allowance used.","retryAfter":3600}`)
		}
	}))
}

func main() {
	srv := fakeAPI()
	defer srv.Close()

	client := ipscanner.NewClient(ipscanner.WithAPIKey("pk_live_..."), ipscanner.WithBaseURL(srv.URL))
	for ev, err := range client.Bulk.Stream(context.Background(), ipscanner.BulkParams{IPs: []string{"1.1.1.1", "8.8.8.8"}}) {
		if err != nil {
			panic(err)
		}
		switch ev.Type {
		case "result":
			fmt.Println(ev.Index, ev.IP, ev.Grade)
		case "done":
			fmt.Println("complete:", ev.Complete())
		}
	}
}
Output:
0 1.1.1.1 B
1 8.8.8.8 A
complete: true
type ChainLink struct {
	ID                  int64     `json:"id"`
	Ts                  time.Time `json:"ts"`
	IP                  string    `json:"ip"`
	ClaimedJurisdiction string    `json:"claimedJurisdiction"`
	NetworkOrigin       string    `json:"networkOrigin"`
	Anonymized          bool      `json:"anonymized"`
	Method              string    `json:"method"`
	Confidence          float64   `json:"confidence"`
	PolicyAction        string    `json:"policyAction"`
	EntryHash           string    `json:"entryHash"`
	PrevHash            string    `json:"prevHash"`
	PrevID              int64     `json:"prevId"`
	EntryOK             bool      `json:"entryOk"`
	LinkOK              bool      `json:"linkOk"`
}

ChainLink is one attestation with its hash-chain status.

type ChainPage

type ChainPage struct {
	Links         []ChainLink    `json:"links"`
	NextBefore    int64          `json:"nextBefore"`
	Total         int64          `json:"total"`
	Summary       ChainSummary   `json:"summary"`
	Anchor        *Anchor        `json:"anchor"`
	Jurisdictions []Jurisdiction `json:"jurisdictions"`
	AsOf          time.Time      `json:"asOf"`
}

ChainPage is a page of attestation chain links.

type ChainParams

type ChainParams struct {
	Limit  int
	Before int64
}

ChainParams pages through Provenance.Chain. Zero values are omitted.

type ChainSummary

type ChainSummary struct {
	Allow  int64 `json:"allow"`
	StepUp int64 `json:"step_up"`
	Block  int64 `json:"block"`
}

ChainSummary counts attestations by policy action.

type ChainVerification

type ChainVerification struct {
	OK       bool  `json:"ok"`
	Count    int64 `json:"count"`
	BrokenAt int64 `json:"brokenAt"`
}

ChainVerification is the result of verifying the attestation chain.

type Client

type Client struct {
	IP           *IPService
	Bulk         *BulkService
	Agentscan    *AgentscanService
	Provenance   *ProvenanceService
	Account      *AccountService
	ASNDirectory *ASNDirectoryService
	Crawlers     *CrawlersService
	Edge         *EdgeService
	Sites        *SitesService
	Gate         *GateService
	// contains filtered or unexported fields
}

Client talks to the IPScanner API. Create one with NewClient and reuse it.

func NewClient

func NewClient(opts ...Option) *Client

NewClient returns a configured Client.

type ConnectionError

type ConnectionError struct {
	Err error
}

ConnectionError is returned when the request could not be completed, including timeouts.

func (*ConnectionError) Error

func (e *ConnectionError) Error() string

func (*ConnectionError) Unwrap

func (e *ConnectionError) Unwrap() error

type Crawler

type Crawler struct {
	Slug        string   `json:"slug"`
	Name        string   `json:"name"`
	Tokens      []string `json:"tokens"`
	Method      string   `json:"method"`
	Verifiable  bool     `json:"verifiable"`
	ReverseDNS  []string `json:"reverseDns,omitempty"`
	PrefixFiles []string `json:"prefixFiles,omitempty"`
	ASNs        []int    `json:"asns,omitempty"`
	Docs        string   `json:"docs,omitempty"`
	Note        string   `json:"note,omitempty"`
}

Crawler is one verifiable crawler and the evidence it is checked against.

type CrawlerList

type CrawlerList struct {
	Crawlers []Crawler `json:"crawlers"`
	Count    int       `json:"count"`
	Note     string    `json:"note"`
}

CrawlerList is the crawler catalogue.

type CrawlerVerification

type CrawlerVerification struct {
	IP            string        `json:"ip"`
	Slug          string        `json:"slug"`
	Crawler       string        `json:"crawler"`
	UserAgent     string        `json:"userAgent,omitempty"`
	Outcome       string        `json:"outcome"`
	Method        string        `json:"method"`
	Summary       string        `json:"summary"`
	Checks        []VerifyCheck `json:"checks"`
	NetworkOrigin string        `json:"networkOrigin"`
	Anonymized    bool          `json:"anonymized"`
	ASN           string        `json:"asn,omitempty"`
	ASNName       string        `json:"asnName,omitempty"`
	Country       string        `json:"country,omitempty"`
	Hostname      string        `json:"hostname,omitempty"`
	Docs          string        `json:"docs,omitempty"`
	CheckedAt     time.Time     `json:"checkedAt"`
}

CrawlerVerification is the result of checking a claimed crawler against its published evidence.

type CrawlersService

type CrawlersService struct {
	// contains filtered or unexported fields
}

CrawlersService lists the crawlers that can be verified. No API key required.

func (*CrawlersService) List

func (s *CrawlersService) List(ctx context.Context) (*CrawlerList, error)

List returns the crawler catalogue.

type Deduction

type Deduction struct {
	Factor string `json:"factor"`
	Points int    `json:"points"`
	Detail string `json:"detail"`
}

Deduction is one factor that lowered a purity score.

type EdgeCheckParams added in v0.2.0

type EdgeCheckParams struct {
	Site          string            `json:"site,omitempty"`
	IP            string            `json:"ip"`
	UserAgent     string            `json:"user_agent,omitempty"`
	JA4           string            `json:"ja4,omitempty"`
	Headers       map[string]string `json:"headers,omitempty"`
	HeadlessFlags map[string]bool   `json:"headless_flags,omitempty"`
	RequestID     string            `json:"request_id,omitempty"`
}

EdgeCheckParams is the input for Edge.Check.

type EdgeCheckResult added in v0.2.0

type EdgeCheckResult struct {
	Class        string            `json:"class"`
	Agent        *AgentscanVerdict `json:"agent"`
	Network      *EdgeNetwork      `json:"network"`
	Site         *EdgeSite         `json:"site"`
	Degraded     []string          `json:"degraded,omitempty"`
	Locked       []string          `json:"locked,omitempty"`
	PlanRequired string            `json:"planRequired,omitempty"`
}

EdgeCheckResult is the response of POST /v1/edge/check. Agent, Network and Site are nil when absent.

type EdgeNetwork added in v0.2.0

type EdgeNetwork struct {
	NetworkClass string   `json:"networkClass"`
	Anonymized   bool     `json:"anonymized"`
	RiskScore    int      `json:"riskScore"`
	Provider     string   `json:"provider,omitempty"`
	VPNProvider  string   `json:"vpnProvider,omitempty"`
	Evidence     []string `json:"evidence,omitempty"`
	Country      string   `json:"country,omitempty"`
	ASN          int64    `json:"asn,omitempty"`
	ASNName      string   `json:"asnName,omitempty"`
}

EdgeNetwork is the network half of an edge check. ASN is a number, not "AS..".

type EdgeService added in v0.2.0

type EdgeService struct {
	// contains filtered or unexported fields
}

EdgeService runs the combined Agentscan and network check for one visitor.

func (*EdgeService) Check added in v0.2.0

func (s *EdgeService) Check(ctx context.Context, params EdgeCheckParams) (*EdgeCheckResult, error)

Check classifies one visitor and attributes it to a site. Metered as 2 requests.

type EdgeSite added in v0.2.0

type EdgeSite struct {
	ID            string `json:"id"`
	Mode          string `json:"mode"`
	PolicyVersion int    `json:"policyVersion"`
}

EdgeSite is the site an edge check was attributed to.

type ExportParams

type ExportParams struct {
	From string
	To   string
}

ExportParams bounds Provenance.Export. From and To accept RFC3339 or YYYY-MM-DD.

type GateNetwork added in v0.2.0

type GateNetwork struct {
	Classification string `json:"classification"`
	Anonymized     bool   `json:"anonymized"`
	Provider       string `json:"provider"`
	VPNProvider    string `json:"vpn_provider"`
}

GateNetwork is the network of a gate verification. Provider and VPNProvider are empty when unknown or locked.

type GateService added in v0.2.0

type GateService struct {
	// contains filtered or unexported fields
}

GateService redeems form gate tokens on the site's server.

func (*GateService) Verify added in v0.2.0

Verify redeems a gate token. It sends no API key and is never retried.

type GateVerification added in v0.2.0

type GateVerification struct {
	Success      bool        `json:"success"`
	Class        string      `json:"class"`
	Action       string      `json:"action"`
	Mode         string      `json:"mode"`
	Confidence   float64     `json:"confidence"`
	Network      GateNetwork `json:"network"`
	Signals      []string    `json:"signals"`
	Hostname     string      `json:"hostname"`
	IssuedAt     time.Time   `json:"issued_at"`
	IPMatch      bool        `json:"ip_match"`
	Locked       []string    `json:"locked,omitempty"`
	PlanRequired string      `json:"planRequired,omitempty"`
}

GateVerification is the response of POST /v1/gate/verify.

type GateVerifyParams added in v0.2.0

type GateVerifyParams struct {
	Secret   string `json:"secret"`
	Token    string `json:"token"`
	RemoteIP string `json:"remote_ip,omitempty"`
}

GateVerifyParams is the input for Gate.Verify. Secret is the site secret (gs_...).

type Geolocation

type Geolocation struct {
	IP             string   `json:"ip"`
	Country        string   `json:"country"`
	CountryCode    string   `json:"countryCode"`
	City           string   `json:"city"`
	Region         string   `json:"region"`
	PostalCode     string   `json:"postalCode"`
	Latitude       float64  `json:"latitude"`
	Longitude      float64  `json:"longitude"`
	Timezone       string   `json:"timezone"`
	AccuracyRadius int      `json:"accuracyRadius"`
	Locked         []string `json:"locked,omitempty"`
	PlanRequired   string   `json:"planRequired,omitempty"`
}

Geolocation is the response of GET /v1/geo/{ip}. Locked and PlanRequired are only set on the /v1/geo body.

type History

type History struct {
	Events     []HistoryEvent `json:"events"`
	NextBefore int64          `json:"nextBefore"`
	AsOf       time.Time      `json:"asOf"`
}

History is a page of past lookups.

type HistoryEvent

type HistoryEvent struct {
	ID         int64     `json:"id"`
	At         time.Time `json:"at"`
	Target     string    `json:"target"`
	IP         string    `json:"ip"`
	Verdict    string    `json:"verdict"`
	Confidence float64   `json:"confidence"`
	ASN        string    `json:"asn"`
	ASNName    string    `json:"asnName"`
	Country    string    `json:"country"`
	Anonymized bool      `json:"anonymized"`
	Source     string    `json:"source"`
}

HistoryEvent is one past lookup.

type HistoryParams

type HistoryParams struct {
	Limit   int
	Before  int64
	Verdict string
}

HistoryParams filters IP.History. Zero values are omitted.

type IPService

type IPService struct {
	// contains filtered or unexported fields
}

IPService covers single-address detection endpoints.

func (*IPService) ASN

func (s *IPService) ASN(ctx context.Context, ip string) (*ASNInfo, error)

ASN returns the autonomous system of an address.

func (*IPService) Demo

func (s *IPService) Demo(ctx context.Context, ip string) (*LookupResult, error)

Demo runs a keyless, rate-limited lookup.

func (*IPService) Geo

func (s *IPService) Geo(ctx context.Context, ip string) (*Geolocation, error)

Geo returns the location of an address.

func (*IPService) History

func (s *IPService) History(ctx context.Context, params *HistoryParams) (*History, error)

History lists the account's recent lookups. params may be nil.

func (*IPService) Lookup

func (s *IPService) Lookup(ctx context.Context, target string) (*LookupResult, error)

Lookup runs a full detection on an IPv4, IPv6, CIDR or hostname target.

Example
package main

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"

	"github.com/ipscanner/ipscanner-go"
)

func fakeAPI() *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch r.URL.Path {
		case "/v1/ip/lookup":
			io.WriteString(w, `{"target":{"raw":"1.1.1.1","kind":"ipv4","ip":"1.1.1.1"},"networkClass":"hosting",
				"purity":{"score":70,"grade":"B","verdict":"suspect"},"at":"2026-10-06T12:00:00Z"}`)
		case "/v1/ip/bulk":
			io.WriteString(w, `{"type":"meta","total":2}
{"type":"result","ip":"1.1.1.1","grade":"B"}
{"type":"result","index":1,"ip":"8.8.8.8","grade":"A"}
{"type":"done","reason":"complete","total":2,"processed":2}
`)
		case "/v1/user/limits":
			w.WriteHeader(http.StatusTooManyRequests)
			io.WriteString(w, `{"error":"rate_limit_exceeded","reason":"monthly_quota","message":"Allowance used.","retryAfter":3600}`)
		}
	}))
}

func main() {
	srv := fakeAPI()
	defer srv.Close()

	client := ipscanner.NewClient(ipscanner.WithAPIKey("pk_live_..."), ipscanner.WithBaseURL(srv.URL))
	res, err := client.IP.Lookup(context.Background(), "1.1.1.1")
	if err != nil {
		panic(err)
	}
	fmt.Println(res.Target.IP, res.NetworkClass, res.Purity.Grade)
}
Output:
1.1.1.1 hosting B

func (*IPService) MyIP

func (s *IPService) MyIP(ctx context.Context) (*MyIP, error)

MyIP returns the caller's own address. No API key required.

func (*IPService) Proxy

func (s *IPService) Proxy(ctx context.Context, ip string) (*ProxyInfo, error)

Proxy reports whether an address is a proxy.

func (*IPService) VPN

func (s *IPService) VPN(ctx context.Context, ip string) (*VPNInfo, error)

VPN reports whether an address is a VPN or Tor exit.

func (*IPService) Whois

func (s *IPService) Whois(ctx context.Context, domain string) (*WhoisInfo, error)

Whois returns registration data for a domain.

type Jurisdiction

type Jurisdiction struct {
	Code         string `json:"code"`
	Label        string `json:"label"`
	Law          string `json:"law,omitempty"`
	Strict       bool   `json:"strict"`
	CleanAction  string `json:"cleanAction"`
	MaskedAction string `json:"maskedAction"`
	Rule         string `json:"rule"`
}

Jurisdiction is one policy the engine knows.

type Limits

type Limits struct {
	AccountType string  `json:"accountType"`
	Limit       int64   `json:"limit"`
	Usage       int64   `json:"usage"`
	Remaining   int64   `json:"remaining"`
	ResetDate   string  `json:"resetDate"`
	Daily       *Window `json:"daily"`
	Hourly      *Window `json:"hourly"`
}

Limits is the account's plan and monthly allowance.

type LookupResult

type LookupResult struct {
	Target       Target       `json:"target"`
	Reserved     Reserved     `json:"reserved"`
	Verdict      Verdict      `json:"verdict"`
	Purity       Purity       `json:"purity"`
	NetworkClass string       `json:"networkClass"`
	IsVPN        bool         `json:"isVpn"`
	IsProxy      bool         `json:"isProxy"`
	IsTor        bool         `json:"isTor"`
	Provider     string       `json:"provider,omitempty"`
	VPNProvider  string       `json:"vpnProvider,omitempty"`
	RiskScore    int          `json:"riskScore"`
	NetworkType  string       `json:"networkType,omitempty"`
	Geo          *Geolocation `json:"geo,omitempty"`
	ASN          *ASNInfo     `json:"asn,omitempty"`
	Whois        *WhoisInfo   `json:"whois,omitempty"`
	WhoisStatus  string       `json:"whoisStatus,omitempty"`
	Degraded     []string     `json:"degraded,omitempty"`
	At           time.Time    `json:"at"`
	Locked       []string     `json:"locked,omitempty"`
	PlanRequired string       `json:"planRequired,omitempty"`
}

LookupResult is the full detection result for one target. Fields nulled by the plan are zero and listed in Locked.

type MyIP

type MyIP struct {
	IPv4           string  `json:"ipv4,omitempty"`
	IPv6           string  `json:"ipv6,omitempty"`
	Country        string  `json:"country"`
	CountryCode    string  `json:"countryCode"`
	City           string  `json:"city"`
	Region         string  `json:"region"`
	PostalCode     string  `json:"postalCode"`
	Latitude       float64 `json:"latitude"`
	Longitude      float64 `json:"longitude"`
	Timezone       string  `json:"timezone"`
	AccuracyRadius int     `json:"accuracyRadius"`
	ASN            string  `json:"asn"`
	ASNName        string  `json:"asnName"`
	ASNType        string  `json:"asnType"`
	ASNCountry     string  `json:"asnCountry"`
}

MyIP is the caller's own address with location and network.

type Option

type Option func(*Client)

Option configures a Client.

func WithAPIKey

func WithAPIKey(key string) Option

WithAPIKey sets the API key. Defaults to the IPSCANNER_API_KEY environment variable.

func WithBaseURL

func WithBaseURL(u string) Option

WithBaseURL sets the API base URL. Defaults to IPSCANNER_API_URL or https://ipscanner.io.

func WithHTTPClient

func WithHTTPClient(hc *http.Client) Option

WithHTTPClient sets the underlying HTTP client.

func WithMaxRetries

func WithMaxRetries(n int) Option

WithMaxRetries sets how many times a failed GET request is retried. Defaults to 2.

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout sets the per-request timeout applied when the context has no deadline. Zero disables it.

func WithUserAgent

func WithUserAgent(ua string) Option

WithUserAgent sets the User-Agent header.

type ProvenanceCheckParams

type ProvenanceCheckParams struct {
	IP                  string         `json:"ip"`
	ClaimedJurisdiction string         `json:"claimed_jurisdiction,omitempty"`
	RequestContext      map[string]any `json:"request_context,omitempty"`
}

ProvenanceCheckParams is the input for Provenance.Check.

type ProvenanceCheckResult

type ProvenanceCheckResult struct {
	NetworkOrigin string  `json:"network_origin"`
	Anonymized    bool    `json:"anonymized"`
	Method        string  `json:"method"`
	Confidence    float64 `json:"confidence"`
	PolicyAction  string  `json:"policy_action"`
	AttestationID int64   `json:"attestation_id"`
}

ProvenanceCheckResult is the policy decision and the ID of the attestation written.

type ProvenanceService

type ProvenanceService struct {
	// contains filtered or unexported fields
}

ProvenanceService records and verifies tamper-evident jurisdiction attestations.

func (*ProvenanceService) Chain

func (s *ProvenanceService) Chain(ctx context.Context, params *ChainParams) (*ChainPage, error)

Chain returns a page of attestations, newest first. params may be nil.

func (*ProvenanceService) Check

Check classifies an address against a claimed jurisdiction and writes an attestation.

func (*ProvenanceService) Export

func (s *ProvenanceService) Export(ctx context.Context, params *ExportParams) ([]byte, error)

Export returns the signed evidence export as raw CSV. params may be nil.

func (*ProvenanceService) Jurisdictions

func (s *ProvenanceService) Jurisdictions(ctx context.Context) ([]Jurisdiction, error)

Jurisdictions lists the jurisdictions the policy engine knows.

func (*ProvenanceService) Verify

Verify verifies the account's attestation chain.

func (*ProvenanceService) VerifyAnchored

func (s *ProvenanceService) VerifyAnchored(ctx context.Context) (*AnchoredVerification, error)

VerifyAnchored verifies the chain now and returns the latest anchor.

type ProxyInfo

type ProxyInfo struct {
	IP           string   `json:"ip"`
	IsProxy      bool     `json:"isProxy"`
	IsTor        bool     `json:"isTor"`
	NetworkClass string   `json:"networkClass"`
	Anonymized   bool     `json:"anonymized"`
	Provider     string   `json:"provider"`
	VPNProvider  string   `json:"vpnProvider"`
	RiskScore    int      `json:"riskScore"`
	Evidence     []string `json:"evidence,omitempty"`
	Locked       []string `json:"locked,omitempty"`
	PlanRequired string   `json:"planRequired,omitempty"`
}

ProxyInfo is the response of GET /v1/proxy/{ip}.

type Purity

type Purity struct {
	Score      int         `json:"score"`
	Grade      string      `json:"grade"`
	Verdict    string      `json:"verdict"`
	Deductions []Deduction `json:"deductions"`
}

Purity is the 0-100 cleanliness score and its grade.

type RateLimitError

type RateLimitError struct {
	APIError
	Reason          string
	RetryAfter      time.Duration
	ResetAt         time.Time
	ResetDate       string
	Limit           int64
	Usage           int64
	Remaining       int64
	Needed          int64
	Plan            string
	SoftBlock       bool
	UpgradeURL      string
	RecommendedPlan string
	RateLimit       RateLimitHeaders
}

RateLimitError is returned for HTTP 429 responses. errors.As also matches it as *APIError.

Example
package main

import (
	"context"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"

	"github.com/ipscanner/ipscanner-go"
)

func fakeAPI() *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch r.URL.Path {
		case "/v1/ip/lookup":
			io.WriteString(w, `{"target":{"raw":"1.1.1.1","kind":"ipv4","ip":"1.1.1.1"},"networkClass":"hosting",
				"purity":{"score":70,"grade":"B","verdict":"suspect"},"at":"2026-10-06T12:00:00Z"}`)
		case "/v1/ip/bulk":
			io.WriteString(w, `{"type":"meta","total":2}
{"type":"result","ip":"1.1.1.1","grade":"B"}
{"type":"result","index":1,"ip":"8.8.8.8","grade":"A"}
{"type":"done","reason":"complete","total":2,"processed":2}
`)
		case "/v1/user/limits":
			w.WriteHeader(http.StatusTooManyRequests)
			io.WriteString(w, `{"error":"rate_limit_exceeded","reason":"monthly_quota","message":"Allowance used.","retryAfter":3600}`)
		}
	}))
}

func main() {
	srv := fakeAPI()
	defer srv.Close()

	client := ipscanner.NewClient(ipscanner.WithAPIKey("pk_live_..."), ipscanner.WithBaseURL(srv.URL))
	_, err := client.Account.Limits(context.Background())

	var rl *ipscanner.RateLimitError
	if errors.As(err, &rl) {
		fmt.Println(rl.Reason, rl.RetryAfter)
	}
}
Output:
monthly_quota 1h0m0s

func (*RateLimitError) Error

func (e *RateLimitError) Error() string

func (*RateLimitError) Unwrap

func (e *RateLimitError) Unwrap() error

type RateLimitHeaders

type RateLimitHeaders struct {
	Meter           string
	Limit           int64
	Remaining       int64
	Reset           time.Time
	DailyLimit      int64
	DailyRemaining  int64
	DailyReset      time.Time
	HourlyLimit     int64
	HourlyRemaining int64
	HourlyReset     time.Time
	UsagePercent    int64
	UpgradeHint     string
	UpgradeURL      string
	UpgradePlan     string
	RetryAfter      time.Duration
}

RateLimitHeaders holds the X-RateLimit-* and X-Quota-* response headers.

type Reserved

type Reserved struct {
	Reserved bool   `json:"reserved"`
	Label    string `json:"label,omitempty"`
	Detail   string `json:"detail,omitempty"`
}

Reserved reports whether the address is in a reserved range.

type SitePolicy added in v0.2.0

type SitePolicy struct {
	Site      string            `json:"site"`
	Mode      string            `json:"mode"`
	Policy    map[string]string `json:"policy"`
	Version   int               `json:"version"`
	UpdatedAt time.Time         `json:"updatedAt"`
}

SitePolicy is the policy a site enforces. Policy maps each traffic class to allow, flag or block.

type SitesService added in v0.2.0

type SitesService struct {
	// contains filtered or unexported fields
}

SitesService reads the policies of the account's sites.

func (*SitesService) Policy added in v0.2.0

func (s *SitesService) Policy(ctx context.Context, id string) (*SitePolicy, error)

Policy returns the current policy of a site. Not metered.

type Target

type Target struct {
	Raw       string `json:"raw"`
	Kind      string `json:"kind"`
	IP        string `json:"ip"`
	Hostname  string `json:"hostname,omitempty"`
	Network   string `json:"network,omitempty"`
	Addresses int64  `json:"addresses,omitempty"`
}

Target describes what was looked up.

type UsageSummary

type UsageSummary struct {
	Plan        string    `json:"plan"`
	Used        int64     `json:"used"`
	Limit       int64     `json:"limit"`
	Unlimited   bool      `json:"unlimited"`
	Remaining   int64     `json:"remaining"`
	Percent     float64   `json:"percent"`
	SoftBlocked bool      `json:"softBlocked"`
	BlockedBy   string    `json:"blockedBy"`
	UpgradeHint bool      `json:"upgradeHint"`
	BulkCap     int       `json:"bulkCap"`
	Daily       *Window   `json:"daily"`
	Hourly      *Window   `json:"hourly"`
	PeriodStart string    `json:"periodStart"`
	ResetDate   string    `json:"resetDate"`
	AsOf        time.Time `json:"asOf"`
}

UsageSummary is the account's usage for the current period.

type VPNInfo

type VPNInfo struct {
	IP           string   `json:"ip"`
	IsVPN        bool     `json:"isVpn"`
	IsTor        bool     `json:"isTor"`
	NetworkClass string   `json:"networkClass"`
	Anonymized   bool     `json:"anonymized"`
	Provider     string   `json:"provider"`
	VPNProvider  string   `json:"vpnProvider"`
	RiskScore    int      `json:"riskScore"`
	Evidence     []string `json:"evidence,omitempty"`
	Locked       []string `json:"locked,omitempty"`
	PlanRequired string   `json:"planRequired,omitempty"`
}

VPNInfo is the response of GET /v1/vpn/{ip}.

type Verdict

type Verdict struct {
	Classification string   `json:"classification"`
	Anonymized     bool     `json:"anonymized"`
	Confidence     float64  `json:"confidence"`
	Method         string   `json:"method"`
	Evidence       []string `json:"evidence,omitempty"`
}

Verdict is the network classification of an address.

type VerifyCheck

type VerifyCheck struct {
	Name   string `json:"name"`
	Status string `json:"status"`
	Detail string `json:"detail"`
}

VerifyCheck is one piece of evidence in a CrawlerVerification.

type WhoisInfo

type WhoisInfo struct {
	Domain            string   `json:"domain"`
	Registrar         string   `json:"registrar"`
	RegisteredOn      string   `json:"registeredOn"`
	ExpiresOn         string   `json:"expiresOn"`
	LastUpdated       string   `json:"lastUpdated"`
	Nameservers       []string `json:"nameservers"`
	Status            []string `json:"status"`
	PrivacyProtection bool     `json:"privacyProtection"`
	WhoisStatus       string   `json:"whoisStatus,omitempty"`
}

WhoisInfo is the response of GET /v1/whois/{domain}. WhoisStatus is ok, not_found, timeout, unavailable or unparsed.

type Window

type Window struct {
	Limit     int64     `json:"limit"`
	Used      int64     `json:"used"`
	Remaining int64     `json:"remaining"`
	ResetAt   time.Time `json:"resetAt"`
}

Window is a daily or hourly quota window.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL