Skip to main content

NestAPI Python Client

PyPI Python Version CI License: MIT

A small, fully dynamic sync and async Python client for the NestAPI web services.

The library contains no service names: any method you call becomes a route. New services work the moment they go live, and removed ones need no code change.

Features

  • Dynamic routing: api.ip_info() calls ip-info with no per-service classes or models
  • Sync and async: NestApi and AsyncNestApi share the same routing, parameters and errors
  • Keyword or dict parameters, with validation left to the server
  • Immutable version switching through version()
  • Read-only responses with dot-path access and res["key"] lookups
  • Typed exceptions mapped from the API's error.code
  • Fully typed, and built on httpx with your own client as an option

Requirements

  • Python 3.10 or higher
  • httpx, installed automatically

Installation

pip install nestapi-py

The package is published as nestapi-py and imported as nestapi.

Quick start

from nestapi import NestApi

with NestApi() as api:
    result = api.bmi(age=30, weight=70, height=175)

    print(result["bmi"])             # 22.86
    print(result.get("input.age"))   # 30

Async

AsyncNestApi works exactly like NestApi: routing, parameters, versions, responses and errors are the same. Await every service call, as well as call(), request() and close(). version() is a plain method.

import asyncio

from nestapi import AsyncNestApi


async def main() -> None:
    async with AsyncNestApi() as api:
        result = await api.bmi(age=30, weight=70, height=175)
        print(result["bmi"])  # 22.86

        # Run several calls at the same time
        first, second = await asyncio.gather(
            api.bmi(age=30, weight=70, height=175),
            api.version(2).call("ip-info", {"ip": "8.8.8.8"}),
        )


asyncio.run(main())

Use one AsyncNestApi instance inside one event loop, because its connections are tied to that loop. To inject a client, pass an httpx.AsyncClient.

The examples below use NestApi. With AsyncNestApi, add await to each call.

Routing

The route is built from the method name. Every _ becomes -, every uppercase letter except the first starts a new - segment, and the result is lowercase.

Method Route
ip_info ip-info
search_shazam search-shazam
chatAi, ChatAi chat-ai
chatAI chat-a-i

Names starting with _ are not services and raise AttributeError.

Pass parameters as keyword arguments or as a single dict. The dict form is needed for names that are Python keywords, such as from or in.

api.bmi(age=30, weight=70, height=175)
api.bmi({"age": 30, "weight": 70, "height": 175})

api.convert({"from": "USD", "in": "EUR"})

api.bmi(30, 70)                  # TypeError: arguments must be named
api.bmi({"age": 30}, weight=70)  # TypeError: use one form, not both
api.bmi({1: "a"})                # TypeError: names must be strings

Explicit calls

# GET with the exact route name, no conversion
api.call("ip-info", {"ip": "8.8.8.8"})

# Any other HTTP method; parameters still go in the query string
api.request("POST", "some-service", {"a": 1})

Parameter values

Python value Sent as Notes
True / False x=true / x=false The server rejects 1, 0, True and TRUE
None Not sent Also skipped inside lists and dicts
list / tuple x=1&x=2 Repeated keys. Not yet verified against the live API, as no documented service takes an array input
dict x[key]=1 Same format as the PHP client

Spaces are encoded as %20, and non-ASCII text is sent as UTF-8.

Versions

The default version is 1, and the apis- prefix is added for you. version() returns a copy and leaves the original instance unchanged. A version below 1 raises ValueError, and anything other than an int, including bool, raises TypeError.

api = NestApi(version=2)

v3 = api.version(3)

There is no automatic "latest version" mode. The source.version field in responses is unrelated to the version in the URL.

Responses

A successful call returns a read-only nestapi.Response.

result = api.bmi(age=30, weight=70, height=175)

result.data                  # Dict, list, string or None
result.meta                  # Optional, service-defined
result.source                # {"name": "NestAPI", ...}

result.get("input.age")      # 30
result.get("input.nope", 0)  # 0
result.get("items.0.title")  # Paths can index into lists

result["bmi"]                # 22.86
"bmi" in result              # True
result["nope"]               # KeyError

result.data = {}             # AttributeError
result["bmi"] = 1            # TypeError
del result["bmi"]            # TypeError

A key that exists with a null value gives None, not the default. If data is not a dict or list, get() returns the default value.

Error handling

Branch on error_code, not on the message, because message text can change.

Exception Raised for
ServiceNotFoundError NOT_FOUND: unknown service or API version
ValidationError PARAM_NOT_FOUND, VALIDATION_ERROR
RateLimitError RATE_LIMITED, RATE_LIMIT_EXCEEDED
ServerError Any other code with HTTP status 500 or above
ApiError Base class of the above, and any other code such as INVALID_IP
TransportError Network failure, non-JSON body, or a body outside the API format

TransportError is separate from ApiError, so catch it as well if you want to handle every failure.

from nestapi import ApiError, NestApi, TransportError, ValidationError

try:
    api.bmi(age=500, weight=70, height=175)
except ValidationError as e:
    e.error_code  # "VALIDATION_ERROR"
    e.status      # 422
    e.details     # {"query.age": "Must be less than or equal to 120"}
except ApiError as e:
    ...           # Any other error reported by the API
except TransportError as e:
    ...           # No usable response arrived; the cause is in e.__cause__

details is a list for PARAM_NOT_FOUND and a dict for VALIDATION_ERROR.

Configuration

NestApi() works without any setup. Every option can be set through the constructor:

import httpx

api = NestApi(
    version=1,
    base_url="https://open.nestapi.ir",
    timeout=30,                  # Seconds, default client only
    client=httpx.Client(),       # Optional; httpx.AsyncClient for AsyncNestApi
)

SSL verification is always on in the default client, and there is no option to turn it off.

Closing connections

Call close() or use with NestApi() as api: to release the connection. With the async client, use await api.close() or async with AsyncNestApi() as api:.

  • A client you inject stays yours: close() leaves it open.
  • Copies from version() share the client of the original. Closing a copy does nothing, so the original keeps working.
  • Closing the original closes the shared client, and its copies then raise TransportError.

Testing

pip install -e ".[dev]"
pytest

Releasing

  1. Bump __version__ in src/nestapi/__init__.py and add an entry to CHANGELOG.md.
  2. Commit, then create a GitHub release with a tag such as v1.0.1.
  3. The Publish to PyPI workflow builds the package and uploads it through PyPI trusted publishing.

License

Released under the MIT License.

Metadata

Release files for nestapi-py 1.0.1

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

Source distribution (sdist)

Source distribution for nestapi-py 1.0.1
File Size Uploaded Provenance Actions
nestapi_py-1.0.1.tar.gz 16.9 kB With provenance Details for nestapi_py-1.0.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for nestapi-py 1.0.1
File Interpreter ABI Platform Actions
nestapi_py-1.0.1-py3-none-any.whl Python 3 none any Details for nestapi_py-1.0.1-py3-none-any.whl

Total release size: 28.8 kB

Release files / nestapi_py-1.0.1.tar.gz

Download URL nestapi_py-1.0.1.tar.gz
Size 16.9 kB
Tags Source
SHA-256 checksum
How to use checksums
ea8edcb939bbfd98ba4c90f8a78e829818631f102b3ee19a883c68c6cb05f60f
BLAKE2b-256 checksum
How to use checksums
c5f4d45fb59cfce4a15ef4b124f4a37002fcf1ec721eadd7986ca5efed65f2a2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 3, 2026.

Transparency log

Release files / nestapi_py-1.0.1-py3-none-any.whl

Download URL nestapi_py-1.0.1-py3-none-any.whl
Size 11.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0662059d6a826000aaece2abe90288f8df2f31e9f6f3a000d13dcfd998daa506
BLAKE2b-256 checksum
How to use checksums
6f9883ee71bb3d723f2118101807cc6f4504474abc8d078092d3a8537ff4cc29
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 3, 2026.

Transparency log

Security for nestapi-py 1.0.1

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.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for nestapi_py-1.0.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 3, 2026.

Transparency log

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

1.0.1 This release

2 release files Full 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