ipscanner

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Oct 6, 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
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)
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.1.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"`
}

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"`
}

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"`
	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"`
}

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"`
	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"`
}

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
	// 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 ExportParams

type ExportParams struct {
	From string
	To   string
}

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

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"`
}

Geolocation is the response of GET /v1/geo/{ip}.

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"`
	RiskScore    int          `json:"riskScore"`
	NetworkType  string       `json:"networkType,omitempty"`
	Geo          *Geolocation `json:"geo,omitempty"`
	ASN          *ASNInfo     `json:"asn,omitempty"`
	Whois        *WhoisInfo   `json:"whois,omitempty"`
	Degraded     []string     `json:"degraded,omitempty"`
	At           time.Time    `json:"at"`
}

LookupResult is the full detection result for one target.

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"`
	RiskScore    int    `json:"riskScore"`
}

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 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"`
	RiskScore    int    `json:"riskScore"`
}

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"`
}

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"`
}

WhoisInfo is the response of GET /v1/whois/{domain}.

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