Skip to content
Gridlode
Docs · SDKs

Python and Node.js SDKs

The official clients wrap the REST API with a typed method for every engine, bulk jobs, monitors, webhook verification, typed errors and automatic retries. They use the same API key and credits as raw HTTP calls, and failed calls are still free.

The PyPI and npm releases are rolling out now. If pip install gridlode or npm install gridlode can't find the package yet, call the REST API directly with the examples in the docs; every SDK method maps one-to-one to an endpoint.

PythonNode.js
Packagegridlode on PyPIgridlode on npm
RequiresPython 3.9+ (depends only on httpx)Node 18+ (no dependencies, uses fetch)
ClientsGridlode, AsyncGridlodeGridlode (ESM and CommonJS, TypeScript types included)

Install

pip install gridlode

Create a key in the dashboard and set it as the GRIDLODE_API_KEY environment variable, or pass it to the client. See Authentication.

Quick start

from gridlode import Gridlode

client = Gridlode()  # reads GRIDLODE_API_KEY, or Gridlode(api_key="sk_live_...")

dns = client.domain.dns(domain="gridlode.com", types=["A", "MX"])
print(dns.data["records"])
print(dns.credits_charged, dns.balance)

page = client.url.markdown(url="https://example.com")
serp = client.serp.google(q="best crm for startups", gl="us")
places = client.places.google_maps(q="coffee", location="Austin, TX", limit=40)
reviews = client.reviews.trustpilot(domain="gridlode.com", limit=20)

# Any engine by slug or route, including engines newer than your SDK version
whois = client.run("domain.whois", domain="example.com", max_age=86400)

Every engine is a method named after its slug: domain.dns is client.domain.dns() and serp.bing is client.serp.bing(). Python uses snake_case method names and keyword arguments; Node.js uses camelCase method names and a parameters object. Parameter names are the same as in the API reference, and leaving one out uses the API default.

The namespaces are ai, apps, classifieds, code, contacts, domain, ecommerce, jobs, places, realestate, reviews, serp, social, travel, url and video. When a route starts differently from the slug, that prefix works too: client.search is client.serp (/v1/search/…), client.youtube is client.video and client.github is client.code.

Engines marked may be disabled are under legal review and off by default: calls fail with EngineDisabled, without charge, until the engine is enabled for your account. The AI engines (ai.extract, ai.review_insights) are billed by actual usage and never cost more than their max_credits parameter (150 by default).

Engine methods

EnginePythonNode.js
AI Data Extractionai.extract()ai.extract()
AI Review Insightsai.review_insights()ai.reviewInsights()
App Store App Detailsapps.app_store_app()apps.appStoreApp()
App Store Searchapps.app_store_search()apps.appStoreSearch()
Google Play App Detailsapps.google_play_app()apps.googlePlayApp()
Google Play Searchapps.google_play_search()apps.googlePlaySearch()
AutoScout24 Car Listingsmay be disabledclassifieds.autoscout24()classifieds.autoscout24()
Craigslist Listings Searchmay be disabledclassifieds.craigslist()classifieds.craigslist()
mobile.de Car Listingsmay be disabledclassifieds.mobile_de()classifieds.mobileDe()
OLX Listings Searchmay be disabledclassifieds.olx()classifieds.olx()
GitHub Repositorycode.github_repo()code.githubRepo()
GitHub Repository Searchcode.github_search()code.githubSearch()
GitHub Trendingcode.github_trending()code.githubTrending()
GitHub User & Organizationcode.github_user()code.githubUser()
Company Enrichmentcontacts.company_profile()contacts.companyProfile()
Company Website Findercontacts.company_website()contacts.companyWebsite()
Email Checkercontacts.email_check()contacts.emailCheck()
Email Findercontacts.email_finder()contacts.emailFinder()
Phone Validatorcontacts.phone()contacts.phone()
Emails & Contacts from Websitecontacts.website()contacts.website()
Domain Authoritydomain.authority()domain.authority()
Domain Availabilitydomain.availability()domain.availability()
Referring Domains (Backlinks)domain.backlinks()domain.backlinks()
DNS Lookupdomain.dns()domain.dns()
Email Security Auditdomain.email_security()domain.emailSecurity()
IP Lookup (ASN, Network, Hosting)domain.ip_info()domain.ipInfo()
RDAP Lookupdomain.rdap()domain.rdap()
Reverse Analytics & AdSense ID Lookupdomain.reverse_ids()domain.reverseIds()
Similar Sitesdomain.similar_sites()domain.similarSites()
SSL Certificate Checkdomain.ssl()domain.ssl()
Subdomain Finderdomain.subdomains()domain.subdomains()
Technology Lookupdomain.tech()domain.tech()
WHOIS Lookupdomain.whois()domain.whois()
Amazon Productmay be disabledecommerce.amazon_product()ecommerce.amazonProduct()
Amazon Search Resultsmay be disabledecommerce.amazon_search()ecommerce.amazonSearch()
eBay Item Detailsmay be disabledecommerce.ebay_item()ecommerce.ebayItem()
eBay Search Resultsmay be disabledecommerce.ebay_search()ecommerce.ebaySearch()
Shopify Store Productsecommerce.shopify_store()ecommerce.shopifyStore()
Walmart Productmay be disabledecommerce.walmart_product()ecommerce.walmartProduct()
Walmart Search Resultsmay be disabledecommerce.walmart_search()ecommerce.walmartSearch()
Ashby Job Boardjobs.ashby()jobs.ashby()
Company Careers Page Jobsjobs.company_site()jobs.companySite()
Greenhouse Job Boardjobs.greenhouse()jobs.greenhouse()
Lever Job Boardjobs.lever()jobs.lever()
Angi Pro Searchmay be disabledplaces.angi()places.angi()
BBB Business Search & Profilesplaces.bbb()places.bbb()
Bing Maps Places Searchplaces.bing_maps()places.bingMaps()
Google Maps Places Searchplaces.google_maps()places.googleMaps()
Google Maps Place Photosplaces.google_maps_photos()places.googleMapsPhotos()
Google Maps Place Detailsplaces.google_maps_place()places.googleMapsPlace()
Google Maps Rank Gridplaces.google_maps_rank()places.googleMapsRank()
Yellow Pages Business Searchplaces.yellowpages()places.yellowpages()
Redfin Property Detailsmay be disabledrealestate.redfin()realestate.redfin()
Zillow Property Detailsmay be disabledrealestate.zillow_property()realestate.zillowProperty()
Zillow Listings Searchmay be disabledrealestate.zillow_search()realestate.zillowSearch()
App Store Reviewsreviews.app_store()reviews.appStore()
Booking.com Hotel Reviewsmay be disabledreviews.booking()reviews.booking()
Google Maps Reviewsreviews.google_maps()reviews.googleMaps()
Google Play Reviewsreviews.google_play()reviews.googlePlay()
Tripadvisor Reviews & Business Infomay be disabledreviews.tripadvisor()reviews.tripadvisor()
Trustpilot Reviewsreviews.trustpilot()reviews.trustpilot()
Baidu Searchserp.baidu()serp.baidu()
Bing Searchserp.bing()serp.bing()
Bing Images Searchserp.bing_images()serp.bingImages()
Bing News Searchserp.bing_news()serp.bingNews()
Bing Videos Searchserp.bing_videos()serp.bingVideos()
Brave Searchserp.brave()serp.brave()
Custom Search API Replacementserp.cse()serp.cse()
DuckDuckGo Searchserp.duckduckgo()serp.duckduckgo()
Google Searchserp.google()serp.google()
Google AI Modeserp.google_ai_mode()serp.googleAiMode()
Google Autocompleteserp.google_autocomplete()serp.googleAutocomplete()
Google Booksserp.google_books()serp.googleBooks()
Google Financemay be disabledserp.google_finance()serp.googleFinance()
Google Images Searchserp.google_images()serp.googleImages()
Google Newsmay be disabledserp.google_news()serp.googleNews()
Google Patent Detailsserp.google_patent()serp.googlePatent()
Google Patents Searchserp.google_patents()serp.googlePatents()
Google Scholarmay be disabledserp.google_scholar()serp.googleScholar()
Google Trendsserp.google_trends()serp.googleTrends()
Google Trends Trending Nowserp.google_trends_trending()serp.googleTrendsTrending()
Naver Searchserp.naver()serp.naver()
Yahoo Searchserp.yahoo()serp.yahoo()
Yandex Searchserp.yandex()serp.yandex()
Facebook Pagemay be disabledsocial.facebook_page()social.facebookPage()
Google Ads Transparency Centermay be disabledsocial.google_ads()social.googleAds()
Instagram Profilemay be disabledsocial.instagram_profile()social.instagramProfile()
Meta Ad Librarymay be disabledsocial.meta_ads()social.metaAds()
Pinterest Profile & Boardsocial.pinterest_profile()social.pinterestProfile()
TikTok Ad Librarymay be disabledsocial.tiktok_ads()social.tiktokAds()
TikTok Profilesocial.tiktok_profile()social.tiktokProfile()
TikTok Videosocial.tiktok_video()social.tiktokVideo()
Airbnb Listing Details & Reviewsmay be disabledtravel.airbnb_listing()travel.airbnbListing()
Airbnb Stays Searchmay be disabledtravel.airbnb_search()travel.airbnbSearch()
Google Flights Searchmay be disabledtravel.google_flights()travel.googleFlights()
Google Hotels Searchmay be disabledtravel.google_hotels()travel.googleHotels()
Broken Link Checkerurl.broken_links()url.brokenLinks()
Website Crawler (Markdown for RAG)url.crawl()url.crawl()
GEO / AI-Readiness Auditurl.geo_audit()url.geoAudit()
HTTP Headers & Security Checkurl.headers()url.headers()
Page HTMLurl.html()url.html()
Keyword Densityurl.keyword_density()url.keywordDensity()
Page Links Extractorurl.links()url.links()
URL to Markdownurl.markdown()url.markdown()
Page Metadata & SEO Tagsurl.metadata()url.metadata()
PageSpeed & Core Web Vitals Testurl.pagespeed()url.pagespeed()
Article Extractor & Readabilityurl.readability()url.readability()
Screenshoturl.screenshot()url.screenshot()
Sitemap Parserurl.sitemap()url.sitemap()
YouTube Channelvideo.youtube_channel()video.youtubeChannel()
YouTube Commentsvideo.youtube_comments()video.youtubeComments()
YouTube Searchvideo.youtube_search()video.youtubeSearch()
YouTube Transcriptvideo.youtube_transcript()video.youtubeTranscript()
YouTube Video Detailsvideo.youtube_video()video.youtubeVideo()

The response

Engine methods return the standard response envelope. In Python it is a dict with shortcuts: res.data, res.request_id, res.credits_charged, res.balance, res.cached and res.parameters. In Node.js it is a typed object (EngineResponse<T>) where you read res.data, res.credits and res.cache directly.

Pass max_age (Python) or { maxAge } in the options argument (Node.js), in seconds, to accept a cached answer at a discount. See Credits and billing.

client.account()   # {"account_id": ..., "credits": {"available": 950.0, "held": 0.0}}
client.engines()   # every engine with route, credits, unit and params_schema

Bulk jobs

Run one engine over up to 10,000 inputs in the background. wait polls with a growing interval until the job is done, failed or cancelled; iter_results / iterResults yields one item per input with its params, status, data, error_code and credits_charged. See Bulk jobs and webhooks for limits and billing.

job = client.tasks.create(
    "domain.tech",
    inputs=["shopify.com", "wordpress.org", "vercel.com"],
    field="domain",
    name="Competitor stacks",
    webhook_url="https://example.com/hooks/gridlode",  # optional
)
secret = job.get("webhook_secret")  # returned once; store it

task = client.tasks.wait(job, timeout=900)  # polls with backoff until done, failed or cancelled
for item in client.tasks.iter_results(task):
    if item["status"] == "success":
        print(item["params"]["domain"], len(item["data"]["technologies"]))
    else:
        print(item["params"]["domain"], "failed:", item["error_code"])

csv_text = client.tasks.results(task, format="csv")

Send full parameter objects with items instead of inputs, and use tasks.get, tasks.list and tasks.cancel to manage jobs.

Monitors

A monitor re-runs one engine on a schedule (every 60 minutes or more), keeps a snapshot of the values you watch and records events: change when they differ, alert when a condition starts to hold, recovered when it clears, and error. Events are sent to a signed webhook and, unless you turn it off with notify_email, by email. Each run is billed like a normal request, and create returns estimated_credits_per_month.

client.monitors.templates()  # domain_expiry, ssl_expiry, dns_change, email_security, page_change, serp_rank, app_store_reviews, google_play_reviews, new_reviews

# From a template: alert 21 days before the certificate expires
ssl = client.monitors.create(
    template="ssl_expiry", target="gridlode.com", options={"days": 21},
    webhook_url="https://example.com/hooks/gridlode",
)
secret = ssl.get("webhook_secret")  # returned once; store it

# From any engine: what to run, which values to compare, when to alert
rdap = client.monitors.create(
    engine="domain.rdap",
    params={"domain": "example.com"},
    watch=["dates.expires", "registrar.name", "nameservers"],
    conditions=[{"path": "dates.expires", "op": "days_until_lt", "value": 30, "label": "Expires within 30 days"}],
    interval_minutes=1440,
)

client.monitors.run(rdap)                        # check now
client.monitors.update(rdap, status="paused")    # name, status, interval_minutes, notify_email, webhook_url
client.monitors.events(rdap, limit=20)           # or client.monitors.events() for every monitor
client.monitors.history(rdap)                    # snapshots of the watched values
client.monitors.delete(rdap)

Watch expressions pick values out of the engine's result: a.b.c (a dotted path, list indexes allowed), a[].b (a value across a list), @hash(path), @rank(array, field, needle), @ids(array, field) and @count(path). Condition operators are lt, lte, gt, gte, eq, ne, days_until_lt, contains, not_contains, is_true and is_false.

Verify webhooks

Jobs send task.completed, task.failed or task.cancelled with {"event":...,"task":{...}}. Monitors send monitor.change, monitor.alert, monitor.recovered or monitor.error with the monitor, a summary and the changes. Every delivery is signed with that job's or monitor's webhook_secret in the X-Gridlode-Signature header (t=timestamp,v1=hex). Verify the raw request body, not re-serialized JSON. Signatures older than five minutes are rejected by default.

from flask import Flask, abort, request
from gridlode import webhooks, WebhookVerificationError

app = Flask(__name__)

@app.post("/hooks/gridlode")
def gridlode_hook():
    try:
        event = webhooks.construct_event(
            request.get_data(),                         # the raw body, not parsed JSON
            request.headers.get("X-Gridlode-Signature"),
            WEBHOOK_SECRET,                             # whsec_... from tasks or monitors.create()
        )
    except WebhookVerificationError:
        abort(400)
    if event["event"] == "task.completed":
        download_results(event["task"]["id"])
    elif event["event"] == "monitor.alert":
        notify_on_call(event["monitor"]["name"], event["summary"])
    return "", 204

Use webhooks.verify(payload, header, secret) to get true or false instead of an exception, and webhooks.sign(payload, secret) to build a valid header when testing your endpoint locally.

Errors

Failed calls raise (Python) or reject with (Node.js) a subclass of APIError that carries the HTTP status, the error type, code, message, details and the request_id. All errors share the base class GridlodeError. See Errors for every code.

ClassHTTPWhen
InvalidRequest400A parameter is missing or invalid. details lists the fields.
AuthenticationError401Missing, malformed or revoked key.
InsufficientCredits402Balance too low or monthly spend cap reached.
PermissionDenied403Blocked by robots.txt, or account suspended.
NotFound404Unknown endpoint, task or monitor.
Conflict409Wrong state, for example cancelling a finished job.
TargetError424The target site or registry failed, refused or timed out.
RateLimited429Too many requests. retry_after / retryAfter holds Retry-After in seconds.
ServerError5xxTemporary problem on our side. You were not charged.
EngineDisabled503The engine is switched off for your account, for example one under legal review. Not retried.
APIConnectionError, APITimeoutErrornoneThe API could not be reached, or did not answer in time.
from gridlode import APIError, InsufficientCredits, RateLimited, TargetError

try:
    meta = client.url.metadata(url="https://example.com")
except TargetError as err:          # 424: the site failed or refused; not charged
    print("site failed:", err.code)
except InsufficientCredits:         # 402
    print("top up your balance")
except RateLimited as err:          # 429 after the automatic retries
    print("slow down, retry in", err.retry_after, "s")
except APIError as err:             # anything else from the API
    print(err.status_code, err.code, err.message, err.request_id)

Retries and timeouts

Engine calls, the search endpoint, job creation, monitors and the MCP server share your key's rate limit. Both clients retry HTTP 429 (waiting for Retry-After, up to 60 seconds), 500, 502, 503, 504 and connection failures, by default twice, with jittered exponential backoff. A request that may already have reached the server, such as one that timed out while waiting for the answer, is retried only for GET, so an engine call, job or monitor is never created twice. Target errors (424) and disabled engines are not retried automatically; retry target errors yourself if the site is likely to recover. See Rate limits.

client = Gridlode(timeout=120, max_retries=4)          # seconds
fast = client.with_options(timeout=10, max_retries=0)  # shares the connection pool

Async Python

AsyncGridlode
import asyncio
from gridlode import AsyncGridlode

async def main():
    async with AsyncGridlode() as client:
        domains = ["stripe.com", "shopify.com", "vercel.com"]
        results = await asyncio.gather(*(client.domain.rdap(domain=d) for d in domains))
        for d, r in zip(domains, results):
            print(d, r.data.get("registrar"))

asyncio.run(main())

Configuration

PythonNode.jsEnvironment variableDefault
api_keyapiKeyGRIDLODE_API_KEYrequired
base_urlbaseUrlGRIDLODE_BASE_URLhttps://api.gridlode.com
timeouttimeout90 s (Python) / 90000 ms (Node.js)
max_retriesmaxRetries2
http_clientfetcha new httpx client / global fetch

No SDK for your language? Every engine is a plain HTTPS call, and the OpenAPI spec can generate a client. For no-code tools, see Integrations; for AI agents, the MCP server.