bisibility
Part of bisibility - open-source SEO platform you can self-host and automate. This repository contains the Python SDK for the Bisibility REST API.
Docs · API reference · Roadmap
SDK version: 0.17.1. Published versions are listed on PyPI.
Python SDK for the Bisibility REST API.
Install
pip install bisibility
For local development:
pip install -e .[dev]
Quickstart
import os
from bisibility import BisibilityClient
bisibility = BisibilityClient(api_key=os.environ["BISIBILITY_API_KEY"])
projects = bisibility.list_projects()
project_id = projects.data[0].id if projects.data else None
if project_id:
created = bisibility.create_keywords(
project_id,
{
"keywords": [
{
"keyword": "rank tracker api",
"target_url": "https://example.com/rank-tracker",
"tags": ["api"],
}
]
},
)
keyword_id = created.results[0].keyword.id if created.results else None
if keyword_id:
check = bisibility.run_rank_check_and_wait(keyword_id)
print(check.position, check.ranking_url)
Configuration
import os
from bisibility import BisibilityClient
bisibility = BisibilityClient(
api_key=os.environ["BISIBILITY_API_KEY"],
base_url="https://bisibility.com/api/v1",
project_id=os.environ.get("BISIBILITY_PROJECT_ID"),
timeout=30.0,
)
base_url should point at the API v1 root. Protected methods send
Authorization: Bearer <api_key>. Write methods accept RequestOptions with an
idempotency_key, which maps to the server Idempotency-Key header.
The client accepts project API keys (bsb_key_live_... or bsb_key_test_...) and
personal access tokens (bsb_pat_live_...). For a PAT with multiple project memberships,
set project_id to a project ID returned by list_projects; the client sends it
as X-Bisibility-Project on project-implicit routes. PAT methods include
get_me, create_project, personal-token self-management, project API-key
minting, and webhook CRUD.
Legacy bsk_... and bsp_... credentials are not accepted by the v3 contract.
Async client
AsyncBisibilityClient exposes the same API operations as BisibilityClient
through httpx.AsyncClient. Use async with to close an owned HTTP client, or
call await client.aclose() explicitly:
import os
from bisibility import AsyncBisibilityClient
async def list_all_keywords(project_id: str) -> None:
async with AsyncBisibilityClient(
api_key=os.environ["BISIBILITY_API_KEY"],
project_id=os.environ.get("BISIBILITY_PROJECT_ID"),
) as bisibility:
async for keyword in bisibility.iter_keywords(project_id, {"limit": 100}):
print(keyword.text)
create_async_bisibility_client() is the factory counterpart to
create_bisibility_client(). Async requests preserve the same authentication,
timeouts, retries, validation, errors, and cursor filters as the synchronous
client. Retry delays and run_rank_check_and_wait polling are cancellable and
do not block the event loop.
Public IDs and v3 cursors
Every resource identifier at the HTTP boundary is a strict lowercase public ID:
<prefix>_[a-z][a-z0-9]{23}. The SDK rejects raw database IDs, legacy IDs,
wrong prefixes, uppercase characters, and malformed suffixes before sending a
request. The same validation is applied to typed request bodies, project header,
and ID-bearing API responses. A malformed success response raises
BisibilityResponseError.
The SDK registry is: agr, al, alr, audit, check, cmp, conn, dwh,
ferry, imp, inv, key, kw, mbr, ntf, pat, prj, rcr, sid, sig,
svkw, tag, usr, viw, and we. Import
PUBLIC_ID_PREFIXES or public_id_pattern() when another component needs the
same contract.
List operations accept and return opaque v3 cursors. Pass meta.next_cursor
back unchanged; the SDK rejects legacy, unversioned, and malformed cursors
before a request is sent or a success response is returned.
Cloud transfer supports package versions 6 and 7, plus metadata-only version 5 packages. Project, cloud-import, and transfer-token identifiers follow the same public-ID rules.
The examples below reuse project_id and keyword_id values returned by the
API, as shown in the quickstart, instead of embedding synthetic resource IDs.
The default timeout is 30 seconds per attempt. Pass timeout=None to the client
to opt out globally, or RequestOptions(timeout=None) for one request. The SDK
sends User-Agent: bisibility-sdk-python/<version> unless you supplied a user
agent, and always sends the matching X-Bisibility-Client header. Every request also declares its
origin with X-Bisibility-Source: sdk for usage reporting; a default header you pass to the
client with the same name wins.
Retries
Failed requests are retried automatically up to max_retries times (default 2)
when the failure is an httpx transport error or a 429/503 response. Only
requests that are safe to replay are retried: idempotent
GET/HEAD/PUT/DELETE requests are always eligible, while non-idempotent
POST/PATCH requests are retried only when an Idempotency-Key header is present (set via
RequestOptions(idempotency_key=...)). When
the server sends a Retry-After header in seconds or HTTP-date form, the client
waits that long (capped at 60 seconds); otherwise it uses capped exponential
backoff (0.5s, 1s, 2s, ... up to 8s). Set max_retries=0 to disable retries.
bisibility = BisibilityClient(api_key="bsb_key_live_...", max_retries=3)
from bisibility import RequestOptions
bisibility.create_api_key(
{"name": "CI"},
request_options=RequestOptions(idempotency_key="request-123"),
)
Methods
- Discovery:
get_health,get_liveness,get_readiness,get_open_api,get_capabilities,get_llms_text - Public cost (no auth):
get_provider_rates,get_cost_estimate - Projects:
list_projects,get_project,update_project,delete_project,get_project_defaults,update_project_defaults. A defaults patch replaces the schedule fields (frequency,cron_expression,jitter_minutes,timezone) as a whole, whileserp_depth(10,20,50, or100) andserp_stop_on_matchkeep their stored value when omitted. - API keys:
list_api_keys,create_api_key,revoke_api_key - Keywords:
list_keywords,create_keywords,add_keywords,get_keyword,update_keyword,set_keyword_target_url,delete_keyword,bulk_update_keywords - Keyword research:
research_keywordsfor one seed andget_keyword_metricsfor batches of up to 700 keywords. Both require API write scope because a cache miss can spend provider budget. Passestimate_only=Truefor a free cache-aware dry run andmax_cost_centsfor a best-effort request guard. A dry run answers with a cost-onlyKeywordResearchEstimate; every other call answers with aKeywordResearchResult. Result source diagnostics reportok,failed, orskipped, including a machine-readable reason when applicable. Both methods expose nullable provider metrics, cache metadata, and paid BYO-key cost. - Backlinks:
analyze_backlinksandload_more_backlink_rows. Analyze withestimate_only=Truefor a free dry run that answers with a cost-onlyBacklinksEstimate; every other call answers with aBacklinksSnapshot.load_more_backlink_rowshas no estimate mode and always answers with a snapshot. - Domain overview:
analyze_domain_overview,load_domain_overview_history,load_domain_overview_keywords, andload_domain_overview_pages. Analyze withestimate_only=Truebefore a paid request; every operation that may spend requires an explicitmax_cost_cents, including0for a cache-only attempt. Responses preserve snake_case market, snapshot, module, provider-cost, ranked-keyword, and relevant-page fields. - Rank checks:
list_rank_checks,run_rank_check,run_rank_check_and_wait,get_rank_check_result - Signals:
create_signal,list_project_signals - Alert rules:
list_alert_rules,create_alert_rule,update_alert_rule,delete_alert_rule,list_triggered_alerts,mute_triggered_alert,mark_project_alerts_read - Rank-history export:
export_rank_historyfor typed cursor-paginated JSON or raw CSV text - Sitemap monitors:
list_sitemap_monitors,update_sitemap_monitor. A project has at most one monitor, so a monitor'sidis its project's id. - Team:
list_team_members,list_team_invites,create_team_invite,revoke_project_team_invite,revoke_team_invite - Providers:
list_providers,connect_provider,test_provider_connection,update_provider_settings,set_provider_enabled,set_provider_priority,set_primary_provider(deprecated; useset_provider_priority),disconnect_provider - Saved views:
list_saved_views,create_saved_view,delete_project_saved_view,delete_saved_view - Saved keywords:
list_saved_keywords,create_saved_keywords,delete_saved_keyword.create_saved_keywordsaccepts plain strings orSavedKeywordInputitems and returnssaved_count,duplicate_count, and a per-keywordresultslist - Competitors:
list_competitors,add_competitor,remove_project_competitor,remove_competitor - Notification preferences:
get_notification_preferences,update_notification_preferences - Migration tokens:
list_migration_tokens,mint_migration_token,revoke_project_migration_token,revoke_migration_token - Cloud import:
get_cloud_import_compatibility,import_cloud_export,create_cloud_import_session,upload_cloud_import_chunk,finalize_cloud_import_session
Research workspace
The client provides typed project context, agent report, AI visibility, prompt
comparison, and site audit methods. Agent reports use strict agr_ IDs, and
iter_agent_reports preserves report-kind filters while following v3 cursors.
AI analysis methods require max_cost_cents and distinguish estimates from saved
results.
Cloud import
The client accepts version 6 and 7 packages. Version 7 requires a
location_key for every keyword; version 6 uses legacy market names without
that field. Both versions require history rows to include normalizationVersion,
provider, requestedDepth, position, previousPosition, and rankingUrl
alongside checkedAt. Nullable history fields remain explicit JSON nulls.
Version 5 is accepted only without ranking history. Sessions require version 6 or 7.
get_cloud_import_compatibility returns integer versions advertised by the server,
including versions this SDK cannot yet send.
Every package must use strict snake_case top-level keys and include all five section arrays, even when they are empty. Package, session, chunk, and response identifiers are validated as typed public IDs before a request is sent.
from bisibility import CloudImportPackage, CloudImportSessionCreate
package = CloudImportPackage(
version=7,
project_id=project_id,
keywords=[],
alert_rules=[],
competitors=[],
notification_preferences=[],
saved_views=[],
)
session = CloudImportSessionCreate(
version=7,
chunk_count=1,
source_project_id=project_id,
)
The nested CloudImportKeyword, CloudImportCompetitor, CloudImportAlertRule,
CloudImportNotificationPreference, and CloudImportSavedView models mirror
the API schema. Alert targets are a discriminated union with type="keyword"
or type="tag"; arbitrary fields and legacy aliases are rejected.
List methods return ListResponse[T] with data and meta.next_cursor. Resource
methods return pydantic models matching the Bisibility API response shape.
Keyword research uses a single seed and a result limit of 100, 300, or 500. Metrics requests accept at most 700 keywords per call, matching the REST contract. Split larger batches explicitly so each response retains its own cache counts and provider cost.
Cursor-paginated resources also expose lazy iter_* generators. They preserve
the supplied filters, request pages only as needed, and stop when
meta.next_cursor is None:
from bisibility import ListKeywordsOptions
for keyword in bisibility.iter_keywords(
project_id,
ListKeywordsOptions(tag="Product", limit=100),
):
print(keyword.text)
Iterators are available for keywords, rank checks, signals, API keys (including
project API keys), webhooks, alert rules, triggered alerts, team members, team
invites, providers, saved views, saved keywords, competitors, and migration
tokens.
The async client exposes the same iter_* names as async iterators.
Domain overview
Domain Overview reads public search-index estimates for a domain or subdomain. A cache miss can spend the project's connected provider account, so obtain the current price before explicitly accepting it:
from math import ceil
from bisibility import AnalyzeDomainOverviewOptions, DomainOverviewEstimate
estimate = bisibility.analyze_domain_overview(
project_id,
AnalyzeDomainOverviewOptions(
target="example.com",
location_code=2840,
language_code="en",
estimate_only=True,
),
)
if not isinstance(estimate.data, DomainOverviewEstimate):
raise RuntimeError("Expected a Domain Overview estimate")
report = bisibility.analyze_domain_overview(
project_id,
AnalyzeDomainOverviewOptions(
target="example.com",
location_code=2840,
language_code="en",
max_cost_cents=ceil(estimate.data.estimated_cost_cents),
),
)
History and additional keyword/page loads use their dedicated option models and also require
max_cost_cents. Cached-only callers can pass 0; the API returns a problem response instead of
silently spending when the requested data is not cached.
Backlinks and keyword-research estimates
analyze_backlinks and research_keywords can spend the project's connected provider
account on a cache miss, so both take estimate_only=True for a free, cache-aware dry
run. A dry run answers with a cost-only model that carries no report fields at all, so
an estimate can never be mistaken for an empty profile or an empty result set. Check
which one you received with isinstance:
from math import ceil
from bisibility import AnalyzeBacklinksOptions, BacklinksEstimate, BacklinksSnapshot
estimate = bisibility.analyze_backlinks(
project_id,
AnalyzeBacklinksOptions(target="example.com", target_scope="site", estimate_only=True),
).data
if not isinstance(estimate, BacklinksEstimate):
raise RuntimeError("Expected a backlinks estimate")
# `cost_cents` is `0` while `cached` is true, and equals `estimated_cost_cents` otherwise.
snapshot = bisibility.analyze_backlinks(
project_id,
AnalyzeBacklinksOptions(
target="example.com",
target_scope="site",
max_cost_cents=ceil(estimate.estimated_cost_cents),
),
).data
if not isinstance(snapshot, BacklinksSnapshot):
raise RuntimeError("Expected a backlinks snapshot")
print(snapshot.summary.backlinks_total, len(snapshot.rows))
BacklinksEstimate carries target, target_scope, include_subdomains, cached,
cached_until, provider, cost_cents, estimate, and estimated_cost_cents, and
nothing else. BacklinksSnapshot carries the report and no estimate fields.
load_more_backlink_rows always answers with a snapshot.
To append rows, pass the opaque snapshot_ref from the latest successful analyze
or load-more response unchanged. Although optional in the SDK for compatibility,
the server requires a current reference. Missing or stale references raise
BisibilityApiError with HTTP 409 and problem type
https://bisibility.com/problems/snapshot_expired before any paid I/O.
Each append returns only newly fetched rows and a replacement reference:
from bisibility import LoadMoreBacklinkRowsOptions
all_rows = list(snapshot.rows)
snapshot = bisibility.load_more_backlink_rows(
project_id,
LoadMoreBacklinkRowsOptions(
target=snapshot.target,
target_scope=snapshot.target_scope,
include_subdomains=snapshot.include_subdomains,
limit=100,
snapshot_ref=snapshot.snapshot_ref,
),
).data
all_rows.extend(snapshot.rows)
# Use this updated snapshot.snapshot_ref for the next load-more request.
research_keywords works the same way, without the data envelope:
from bisibility import KeywordResearchEstimate, KeywordResearchOptions
response = bisibility.research_keywords(
project_id,
KeywordResearchOptions(seed="rank tracker", estimate_only=True),
)
if isinstance(response, KeywordResearchEstimate):
for source in response.sources:
print(source.source, source.cost_cents, source.cached)
else:
print(response.total_count, len(response.rows))
A KeywordResearchEstimate reports estimate, cached, cost_cents, provider,
connections, and one {source, cost_cents, cached} entry per planned source. Source
status, returned, and reason belong to KeywordResearchResult, because a dry run
fetches nothing and so has no source outcome to report.
Webhook secret rotation
To rotate a webhook secret, set hmac_secret on WebhookUpdateInput to a
non-empty string. Sending a null, empty, or whitespace-only secret is rejected.
Omit hmac_secret entirely to leave the existing secret unchanged; omitted
fields are not included in the PATCH request.
All SDK-defined exceptions derive from BisibilityError. BisibilityApiError
also exposes is_rate_limit, is_not_found, and retry_after_seconds helpers.
Problem responses follow RFC 9457 and preserve extension members; sensitive
response headers are removed before an API error is exposed.
list_keywords supports country, device, tag, topic, intent,
position_gt/position_lt, search and sort filters via
ListKeywordsOptions (or a plain dict); topic and intent match the keyword
classification fields, case-insensitively.
Signals
Ingest deploy/CMS/API events as signals and read them back per project.
create_signal requires source ("deploy", "cms", or "api") and type
(shaped like category.event, e.g. "deploy.completed"); payload must
serialize to 8KB or less. list_project_signals returns signals newest first
and accepts source, type and a from/to time range (use the from_
field name, or the "from" key in a dict).
from bisibility import CreateSignalInput, ListSignalsOptions
emitted = bisibility.create_signal(
CreateSignalInput(
source="deploy",
type="deploy.completed",
payload={"version": "1.2.3"},
url="https://example.com/releases/1",
)
)
signals = bisibility.list_project_signals(
project_id,
ListSignalsOptions(source="deploy", from_="2026-07-01T00:00:00Z"),
)
Public cost estimates
get_provider_rates and get_cost_estimate are public discovery endpoints and
work without an API key. get_cost_estimate requires keywords and accepts
locations, devices (1-2), frequency ("daily", "weekly",
"monthly"), provider, and a flat-rate option or plan plan selector.
Both return a DataResponse whose data is typed (list[ProviderRate] and
CostEstimate).
from bisibility import BisibilityClient, CostEstimateOptions
anonymous = BisibilityClient() # no api_key needed for these calls
rates = anonymous.get_provider_rates()
estimate = anonymous.get_cost_estimate(
CostEstimateOptions(keywords=248, frequency="daily", provider="dataforseo")
)
print(estimate.data.monthly_cost_usd)
Queued rank checks
How a requested check executes belongs to the deployment, not to the call.
Where a background worker owns execution the server answers 202 Accepted with
the queued run, and where checks run inline it answers 201 Created with the
finished check. run_rank_check returns that union.
started = bisibility.run_rank_check(keyword_id)
if isinstance(started, RankCheckRunQueued):
print(f"Queued as run {started.id}")
Every rank check carries the run_id of the run that produced it, which is how
a queued run is followed to its result. run_rank_check_and_wait does that
polling and returns the finished check, raising BisibilityTimeoutError when
the deadline passes.
check = bisibility.run_rank_check_and_wait(keyword_id, timeout_seconds=120)
print(check.position, check.ranking_url)
async_mode is retained for compatibility and no longer changes what the
server does.
Typed Inputs
Plain dictionaries are supported for request bodies, and pydantic input models are available when you want validation and editor completion.
from bisibility import CreateKeywordInput, CreateKeywordsBatch, KeywordScheduleInput
created = bisibility.create_keywords(
project_id,
CreateKeywordsBatch(
keywords=[
CreateKeywordInput(
keyword="rank tracker",
target_url="https://example.com/rank-tracker",
tags=["api"],
schedule=KeywordScheduleInput(
frequency="daily",
cron_expression=None,
),
)
]
),
)
New app-surface endpoints also have typed request models. Plain dictionaries remain supported.
from bisibility import (
AlertRuleInput,
ApiKeyCreateInput,
ConnectProviderInput,
ProjectDefaultsPatch,
ProviderCredentialsInput,
UpdateProjectInput,
)
project = bisibility.update_project(project_id, UpdateProjectInput(name="Marketing site"))
defaults = bisibility.update_project_defaults(
project_id,
ProjectDefaultsPatch(frequency="daily", location_key="US/Texas/Austin"),
)
api_key = bisibility.create_api_key(ApiKeyCreateInput(name="CI"))
rule = bisibility.create_alert_rule(
project_id,
AlertRuleInput(
channels=["email"],
condition_type="threshold",
name="Ranking drop",
target_type="all",
threshold_position=10,
),
)
provider = bisibility.connect_provider(
project_id,
"serpapi",
ConnectProviderInput(
credentials=ProviderCredentialsInput(api_key="serpapi-secret"),
priority=0,
),
)
# `connect_provider` sends `priority` with the connect request. `0` promotes the provider
# and renumbers the fallback chain; any other value reorders it. Omit it to keep a
# reconnected provider's place and append a new one. Legacy `primary=True` maps to zero;
# `primary=False` is a no-op.
# For "plausible", `login` is the site domain configured in Plausible (its `site_id`) and
# defaults to the project domain when omitted, `api_key` is the Stats API token, and the
# optional `endpoint` points at a self-hosted instance API.
analytics = bisibility.connect_provider(
project_id,
"plausible",
ConnectProviderInput(
credentials=ProviderCredentialsInput(
api_key="plausible-stats-api-token",
login="example.com",
endpoint="https://plausible.example.com/api",
),
),
)
# A successful `test_provider_connection` reports "Connected.", or
# "Connected · <detail>." for analytics providers, where the detail names the
# resolved property or site.
print(bisibility.test_provider_connection(project_id, "plausible").message)
Errors
Non-2xx API responses raise BisibilityApiError. The RFC problem details body is
available on error.problem when the server returns one.
from bisibility import BisibilityApiError
try:
bisibility.get_keyword("kw_z9y8x7w6v5u4t3s2r1q0p9n8")
except BisibilityApiError as error:
print(error.status, error.problem.detail if error.problem else error.body)
License
Licensed under the Apache License, Version 2.0. See LICENSE and NOTICE.
Saved reports and provider budgets
Saved report reads never invoke a provider. Use fresh_until to determine freshness;
state is fresh or stale, while a domain report keeps its data outcome in data_state.
Own-key and credit budgets are independent. Omit a field to keep it and explicitly clear
a surface to remove its budget. Credit budgets always use cents.
saved = client.list_stored_research_reports(project_id)
report = client.get_stored_research_report(project_id, "keyword_research", {"seed": "example", "result_limit": 100})
budgets = client.list_provider_budgets(project_id)
client.update_provider_budgets(project_id, "dataforseo", {"own": {"app": None}, "credits": {"programmatic": {"amount_per_month": 500, "unit": "cents"}}})
The same methods are available with await on AsyncBisibilityClient.
AI tracking
AI tracking methods manage project topics, immutable prompt revisions, disabled-by-default
schedules, runs, samples, history, comparable trends, evidence exports, and editable prompt
suggestions. Tracking resource IDs use ait_, aip_, apr_, ais_, air_, and asm_.
Preview before launching paid work, pass the preview credential/budget/consent revisions, and
provide explicit consent plus an Idempotency-Key for launch or retry. Preview never submits a
provider task. Preserve the returned decimal USD strings, null amounts, unknown measurement
states, original observation times, and cited sources. Model API tests and consumer observations
remain distinct evidence sources.
The free AI research catalog supplies current model and locale choices. Prompt comparison
supports hard caps and explicitly consented provider actual-cost mode. Actual-cost mode requires
non_guaranteed_estimate_v1 acknowledgement and an advisory estimate limit; execution also
requires the estimate credential reference and a stable UUID. An advisory limit is not a
guaranteed maximum charge.
Model prompt generation uses ai_tracking_suggestions_preview followed by
ai_tracking_suggestions_generate. Review all five context fields, selected competitors, model
and market. The complete reviewed snapshot must fit within 5000 serialized Unicode characters.
Generation requires the unexpired frozen preview, explicit paid consent and a stable UUID
idempotency_key request option. Forecasts are not guaranteed maximum costs. Accepted edits
retain generation_reference; provider dataset prompts require provider_dataset_reference.
Generated drafts have no measured popularity, and unknown USD costs remain null.
Response evidence compatibility
Rank checks expose optional observation_completeness, and keywords expose optional
latest_check and latest_successful_check. Older responses can omit these fields.
Only complete coverage supports absence within the requested depth; a positive position
remains valid with unknown or truncated coverage. A newer failure retains the prior success.
Site backlinks normally have 12 history months. A successful partial snapshot has
history: [] and history_unavailable: true; preserve its summary, rows and confirmed cost.
Page scope does not request history. Missing availability metadata uses the legacy default.
A failed summary remains an HTTP error with current-response evidence in problem details:
cost_cents: null is an unknown total, while known_summary_cost_cents is confirmed.
Read rank-check runs
list_rank_check_runs(project_id, options={"segment": "history", "limit": 50})
and get_rank_check_run(project_id, run_id) read existing project runs on both
sync and async clients. They include blocked, cancelled and deferred outcomes
without requiring a completed rank-check row or launching a measurement. Lists
support history or planned segments, comma-separated status, cursor and
limits from 1 to 200. Preserve the returned cursor when continuing a list.
The typed run exposes terminal and polling state, nullable usage and native
estimates, and counts; future status and outcome values remain readable.
Versioned retained history export
Opt in with version 2 to retain report quality, metric versions, frozen selection and signed continuation cursors. Keep stream, basis, from/to and keyword selection unchanged on every page. The default remains legacy v1 rows. Observations include bounded normalized items; opaque CID strings retain all original digits. The recorded-at attempts stream also contains actual run/item variants without checks, including cancelled, deferred and blocked outcomes. Full-project exports retain explicit project-only runs; selected exports report unresolved membership exclusions. CSV contains a complete bounded report manifest and accepts no cursor. Oversized sources fail with 413, and changed signed snapshots fail with 409.
See the rank-history contract.
Retained Local monitor reads
list_local_monitor_runs, get_local_monitor_run_result,
list_local_monitor_point_listings, list_local_monitor_point_history, and
export_local_monitor_run are available on the sync and async clients.
They only issue GET requests: setup, paid collection, schedules and rematching are separate actions.
Use lmn_… monitor, lmr_… run and lmt_… target IDs with the project's prj_… ID.
Selections accept exact UTC millisecond instants (2026-10-12T12:00:00.000Z);
history cursors are opaque strings of 1–8000 characters. Do not reconstruct them.
result = client.get_local_monitor_run_result(project_id, monitor_id, run_id,
options={"as_of": "2026-10-12T12:00:00.000Z"})
csv = client.export_local_monitor_run(project_id, monitor_id, run_id,
options={"version": "1", "format": "csv"})
History time is occurrence_created_at, not provider observation time.
Nullable timestamps/ranks/axes and freshness policy_epoch remain explicit;
unknown or stale evidence is never replaced with absence or rank zero.
CID stays an opaque string, including values beyond safe numeric ranges.
Response metadata can add new values without making old clients reject the page;
report version and resource ID namespaces remain strict.
JSON export preserves its camelCase retained-report contract; CSV is the exact
server export text. All five methods honor the existing authentication, timeout,
cancellation, error and request-options behavior.
Project defaults previews require the server capability reviewed_project_defaults: v1. Call preview_project_defaults with a draft, show its signed preview and impact, then explicitly submit the same draft with ProjectDefaultsReview. Preserve the returned revision as a string and reuse one command key for retries. Async clients expose the same two-step flow. Preview never changes project settings.
Metadata
Release files for bisibility 0.17.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bisibility-0.17.1.tar.gz | 142.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bisibility-0.17.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 225.1 kB
Release files / bisibility-0.17.1.tar.gz
| Download URL | bisibility-0.17.1.tar.gz |
|---|---|
| Size | 142.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5ce26575a563c28164fe3eb78323e8d30560cde560082c4ac13ff3754f194d26
|
|
BLAKE2b-256 checksum How to use checksums |
c7adffcdb7204840c5376d0f41611a723c75d2a35e8676624d46f3c4888c9595
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","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 / bisibility-0.17.1-py3-none-any.whl
| Download URL | bisibility-0.17.1-py3-none-any.whl |
|---|---|
| Size | 82.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e9620324fd52dadc3d6d58f28a3b333195afcaf3a38d088a0a69d1d4b4f7a531
|
|
BLAKE2b-256 checksum How to use checksums |
17bda40c07356d770ac8acf955b22ea9c47a78ddc2d6386f3d7156d63a90ab59
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","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}
|