Skip to main content

nanoserp

A tiny, free Python library and CLI for web search and page scraping. No API keys, no accounts, no rate limit tiers -- just search and scrape.

Built on top of DuckDuckGo's HTML endpoint and markdownify, nanoserp is designed for small-scale coding agents and agentic Python applications that need lightweight, zero-config access to web search and content extraction.

Install

pip install nanoserp

Requires Python 3.11+.

Claude Code Skill

To install nanoserp as a skill for Claude Code and other coding agents:

npx skills add https://github.com/fkodom/nanoserp --skill nanoserp

CLI

Search

nanoserp search "python web scraping"

Filter results by time range (d/w/m/y):

nanoserp search "python web scraping" --date-filter w

Paginate through results with --offset:

nanoserp search "python web scraping" --offset 10

Scrape

nanoserp scrape "https://example.com"

Prints the page content as markdown, followed by an aggregated list of all links found on the page.

Python API

Search

from nanoserp import search, DateFilter

# Basic search
result = search("python web scraping")

for r in result.results:
    print(r.title, r.url, r.snippet)

# Filter by time range
result = search("python web scraping", date_filter=DateFilter.WEEK)

# Pagination
page1 = search("python web scraping")
page2 = search("python web scraping", offset=len(page1.results), vqd=page1.vqd)

The search function returns a SearchResponse:

Field Type Description
query str The original query string
results list[SearchResult] Parsed search results
vqd str | None Session token for pagination

Each SearchResult contains:

Field Type Description
title str Result title
url str Result URL
snippet str Text snippet
date datetime | None Publish date, if available

Scrape

from nanoserp import scrape

result = scrape("https://example.com")

print(result.markdown)     # Full page content as markdown
for link in result.links:
    print(link.text, link.url)

The scrape function returns a ScrapeResponse:

Field Type Description
url str The scraped URL
markdown str Page content converted to markdown
links list[ScrapeLink] All links found on the page

Error Handling

All errors inherit from NanoserpError:

from nanoserp import search
from nanoserp.exceptions import NanoserpError, RateLimitError

try:
    result = search("test")
except RateLimitError:
    # DuckDuckGo returned HTTP 429
    ...
except NanoserpError as e:
    # Any other HTTP or parsing error
    print(e.message)

Limitations

  • Search results come from DuckDuckGo's HTML endpoint, which is not an official API. The response format may change without notice.
  • DuckDuckGo may rate-limit or block requests under heavy use. This tool is intended for small-scale, low-frequency usage.
  • Scraping respects the target server's response but does not check robots.txt.

For Developers

Setup

This project uses uv for dependency management, but any virtual environment or package manager (pip, venv, poetry, conda) will work.

# Create and activate a virtual environment
uv venv --python 3.12
source .venv/bin/activate

# Install development dependencies and pre-commit hooks
uv sync --extra dev
pre-commit install

Verification

Before committing, make sure all checks pass:

# Lint and format
uv run ruff check --fix . && uv run ruff format .

# Type check
uv run ty check .

# Unit tests
uv run pytest

# Include integration tests (makes real HTTP requests)
uv run pytest --slow

Tooling

Tool Description
ruff Linting and formatting
ty Static type checking
pytest Unit and integration testing
pre-commit Git hook management

Publishing

To publish to PyPI, move .github/disabled-workflows/publish.yaml.disabled to .github/workflows/publish.yaml and set PYPI_API_TOKEN in the repo secrets. Then tag a new release, and GitHub Actions will build and publish the package automatically.

Metadata

Release files for nanoserp 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for nanoserp 0.1.0
File Size Uploaded Provenance Actions
nanoserp-0.1.0.tar.gz 21.2 kB Without provenance Details for nanoserp-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for nanoserp 0.1.0
File Interpreter ABI Platform Actions
nanoserp-0.1.0-py3-none-any.whl Python 3 none any Details for nanoserp-0.1.0-py3-none-any.whl

Total release size: 35.5 kB

Release files / nanoserp-0.1.0.tar.gz

Download URL nanoserp-0.1.0.tar.gz
Size 21.2 kB
Tags Source
SHA-256 checksum
How to use checksums
24fbf60b72190d5ec4fc8bb5a083934b98e12ba23f0939fb2774ceb18c6f18e2
BLAKE2b-256 checksum
How to use checksums
aadf195bfd9d3bc8034c22c8889ec5eb6ec615eadaeb367bd768932069ad5e91
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / nanoserp-0.1.0-py3-none-any.whl

Download URL nanoserp-0.1.0-py3-none-any.whl
Size 14.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
848c10d1d903770658a80d6d80b84cd66426a9a5facdd75db94c17f717a2490f
BLAKE2b-256 checksum
How to use checksums
6fccf7a8c871635606bae8be42156d71211455eb772d46560ba71ef262e5ba32
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for nanoserp 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page