Skip to main content

binaaz-python-sdk

Python bina.az Typed Async Ruff License

Typed Python client for bina.az, the Azerbaijani real estate site. Sign in with email and password or with an SMS code, post, edit, prolong and delete listings, upload and rotate photos, read the catalog of cities, districts and metro stations. Sync and async, with retry rules built so a listing or a payment is never sent twice.

pip install binaaz-python-sdk

This is not an official bina.az product. It talks to the same endpoints the bina.az website uses, with its own User-Agent. It does not solve captchas or Cloudflare challenges: if bina.az answers with a challenge page you get BlockedError.

Sign in

from binaaz_sdk import BinaAz

with BinaAz() as bina:
    bina.auth.login("agency@example.az", "password")
    saved = bina.cookie

Phone accounts sign in with a code that bina.az sends by SMS:

sent = bina.auth.send_code("050 123 45 67")
profile = bina.auth.verify_code("050 123 45 67", "1234")

bina.cookie is the signed in session. Keep it somewhere safe and pass it back later, so you do not sign in on every run:

bina = BinaAz(cookie=saved)
bina.profile()

A cookie copied from a browser works too, as a header value, a {"name": "value"} mapping or a cookie export list. Cloudflare cookies are dropped, they are never stored or sent. Cookies that bina.az renews are picked up from every answer.

Post a listing

from binaaz_sdk import BinaAz, Category, ContactType, Listing

photos = bina.photos.upload_many(["1.jpg", "2.jpg", "3.jpg", "4.jpg"])

result = bina.items.create(Listing(
    category_id=Category.APARTMENT_OLD_BUILDING,
    is_leased=False,
    price=120000,
    rooms=3,
    area={"value": 85},
    floor={"value": 4, "total": 9},
    location={"address": "Nizami street 10", "city_id": "1", "latitude": 40.379, "longitude": 49.846},
    contact={"type": ContactType.AGENT, "name": "Agency"},
    photo_ids=[p.id for p in photos],
    description="Renovated three room flat near the metro.",
))
result.id, result.url, result.state

bina.az takes 4 to 30 photos on a listing. A new listing starts as pending and moves to published, rejected or awaiting_payment. When bina.az refuses it, ItemError.status tells why: RESTRICTED (confirm the phone number first), UNVERIFIED, INVALID or FAILURE. Field problems are in ItemError.errors.

Manage listings

page = bina.items.list("published", first=50)
everything = bina.items.all()
item = bina.items.get("4123456")

bina.items.update("4123456", listing)
bina.items.prolong("4123456")
bina.items.delete("4123456")

list follows bina.az cursors (page.next_cursor), all reads every page. Tabs are pending, published, rejected, expired and awaiting_payment. An update of a listing under moderation raises ItemError with status="LOCKED" and locked_until. Listing management needs a company profile (agencies and residences); other profiles get NotSupportedError.

Paid services

for service in bina.items.paid_services("4123456"):
    service.title, service.options, service.payment_methods

payment = bina.items.buy_service("4123456", service.id, "wallet", option_id=service.options[0].id, confirm=True)
payment.redirect_url
bina.items.pay("4123456", confirm=True)

Anything that costs money needs confirm=True, otherwise InvalidRequestError is raised before a request is made. A card payment comes back with redirect_url, the page where the payment is finished.

Photos and catalog

photo = bina.photos.upload(open("cover.jpg", "rb"))
photo = bina.photos.rotate(photo.id, 90)

bina.catalog.categories()
bina.catalog.cities()
bina.catalog.regions("1")
bina.catalog.townships("4")
bina.catalog.landmarks("1", 40.379, 49.846)

Uploads take bytes, a path or a file object. Rotating returns a new photo id; use that one in the listing. Catalog calls need no session.

Safety

Situation What the client does
Connection refused, nothing sent Retries, then NotSentError
Timeout, cut connection or 5xx on create, update, delete, prolong, payment, sign in or photo upload No retry, OutcomeUnknownError. Check the account before trying again
Same failure on a read Retries with backoff, then TransportError or ServerError
429 Waits Retry-After and retries, the request was not processed
SMS login service answers with an error page instead of a known error Retried up to code_attempts (3), the login service drops some answers
SMS code sent only on a retry CodeSent.retried is True: more than one SMS may arrive, use the latest code
SMS code rejected after a retry CodeRejectedError says the code may have been used by the earlier attempt
Timeout on the SMS login service Not resent, OutcomeUnknownError
Cloudflare challenge page BlockedError, never solved or retried
Paid service or paying for a listing without confirm=True InvalidRequestError, nothing is sent
Fewer than 4 or more than 30 photos InvalidRequestError before anything is sent
A failed sign in The previous session is kept
Plain http address outside localhost ConfigurationError, unless allow_insecure_http

Endpoints

Namespace Methods
auth login send_code verify_code profile
items create update delete prolong get list all paid_services buy_service pay
photos upload upload_many rotate
catalog categories cities regions townships landmarks
client profile cookie set_cookie clear_session graphql

AsyncBinaAz has the same methods, awaited. graphql(query, variables) sends any other query with the session.

Errors

BinaAzError is the base. Answers raise APIError subclasses: BadRequestError, AuthenticationError (the session is not signed in), NotFoundError, RateLimitError, ServerError and BlockedError. GraphQL errors raise GraphQLError with errors, refused listings raise ItemError with status, errors and locked_until, a wrong SMS code raises CodeRejectedError. Each carries status_code, message and the raw body.

Config

Pass values in code, or set BINAAZ_COOKIE, BINAAZ_TIMEOUT, BINAAZ_MAX_RETRIES, BINAAZ_CODE_ATTEMPTS, BINAAZ_USER_AGENT and the rest, or put them in binaaz.toml (example). Every path is a setting (BINAAZ_ENDPOINTS__GRAPHQL=...). Messages live in messages.yaml and can be replaced with messages.use(...).

Develop

pip install -e ".[dev]" && pytest && ruff check . && mypy src

License

MIT

Metadata

Release files for binaaz-python-sdk 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 binaaz-python-sdk 0.1.0
File Size Uploaded Provenance Actions
binaaz_python_sdk-0.1.0.tar.gz 29.7 kB Without provenance Details for binaaz_python_sdk-0.1.0.tar.gz

Built distribution (wheel)

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

Total release size: 56.4 kB

Release files / binaaz_python_sdk-0.1.0.tar.gz

Download URL binaaz_python_sdk-0.1.0.tar.gz
Size 29.7 kB
Tags Source
SHA-256 checksum
How to use checksums
8f056157bb2c3cd3e47828f68e3b5b80b1a0673ce9d52a96b219e90f381a88a9
BLAKE2b-256 checksum
How to use checksums
a65fc2b4a62723021a6962034aaf100550501d8dce8a68a41b5cef23c361f26a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

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

Download URL binaaz_python_sdk-0.1.0-py3-none-any.whl
Size 26.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a9a8d231c515c4fe6d9d89f40f8824b87938d50f657ee7d2e406c3166081a7db
BLAKE2b-256 checksum
How to use checksums
4d71c7400be59cbf3cb3d8eb02be49e74803afe2786d177c7bcd718b0dad7072
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Security for binaaz-python-sdk 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