API reference

Auto-generated from source docstrings and type hints.

Client

class ogu_api.OGUClient(*, proxy=None, base_url=None, timeout_seconds=None, max_retries=None, retry_backoff_seconds=None, client_identifier=None, config=None, session=None)[source]

Bases: object

The top-level OGUsers client.

Aggregates 11 resource groups (session, users, usercp, reputation, credits, messages, notifications, feed, threads, search, members) over a single tls_client session with Cloudflare-friendly TLS fingerprints.

Use as an async context manager — the underlying session is closed on __aexit__:

>>> async with OGUClient() as client:
...     await client.session.login('username', 'password')
...     user = await client.users.get_by_username('forgivenforget')
Parameters:
  • proxy (str | None) – Optional proxy string. Accepts user:pass@host:port, host:port:user:pass, or plain host:port; auto-normalized to http://....

  • base_url (str | None) – Override the forum base URL (default https://oguser.com).

  • timeout_seconds (float | None) – Per-request timeout (default 30).

  • max_retries (int | None) – How many times to retry on 429 and 5xx (default 0).

  • retry_backoff_seconds (float | None) – Base delay for exponential backoff (default 0.5).

  • client_identifier (str | None) – TLS fingerprint identifier for tls_client (default 'chrome131').

  • config (HttpClientConfig | None) – Provide a fully-built HttpClientConfig instead of the individual kwargs above.

  • session (tls_client.Session | None) – Provide a pre-configured tls_client.Session. The client will not close it automatically.

session

Login / logout / login form.

Type:

SessionResource

users

Profile lookup.

Type:

UsersResource

usercp

Account settings.

Type:

UserCPResource

reputation

Send reputation.

Type:

ReputationResource

credits

Send / track credits.

Type:

CreditsResource

messages

Private messages.

Type:

MessagesResource

notifications

Notifications + alerts.

Type:

NotificationsResource

feed

Explore + home feeds.

Type:

FeedResource

threads

View threads / forums; reply; create.

Type:

ThreadsResource

search

Forum-wide search.

Type:

SearchResource

members

Member directory.

Type:

MembersResource

close()[source]

Close the underlying HTTP session.

Called automatically when used as an async context manager. Only closes the session if the client created it (i.e. you didn’t pass your own session to __init__).

Return type:

None

property http: HttpClient

The underlying HttpClient. Use for endpoints not yet wrapped.

property cookies: Any

The session cookie jar (tls_client.Session.cookies).

Iterable of http.cookiejar.Cookie objects. Use for persistence:

>>> jar = {c.name: c.value for c in client.cookies}

HTTP layer

class ogu_api.HttpClient(*, proxy=None, config=None, session=None)[source]

Bases: object

Async wrapper around tls_client.Session.

Runs the synchronous tls_client calls in a worker thread via asyncio.to_thread, applies retries on 429 / 5xx, and maps HTTP status codes to typed exceptions from ogu_api.errors.

You typically don’t construct this directly — OGUClient does it for you. Drop down to client.http when you need to hit an endpoint not yet covered by a resource.

Parameters:
  • proxy (str | None) – Optional proxy string (see Proxy for accepted forms).

  • config (HttpClientConfig | None) – Configuration. None for defaults.

  • session (tls_client.Session | None) – Pre-configured tls_client.Session. If provided, the client will not close it on shutdown.

async get(path, *, query=None, extra_headers=None, allow_redirects=True)[source]

Convenience wrapper for request('GET', ...).

Parameters:
Return type:

Response

async post(path, *, data=None, json_body=None, query=None, extra_headers=None, allow_redirects=True)[source]

Convenience wrapper for request('POST', ...).

Parameters:
Return type:

Response

async request(method, path, *, query=None, data=None, json_body=None, extra_headers=None, allow_redirects=True)[source]

Issue an HTTP request through the underlying tls_client.Session.

Parameters:
  • method (str) – HTTP verb ('GET', 'POST', …).

  • path (str) – Request path (relative to base_url) or absolute URL.

  • query (Mapping[str, Any] | None) – Query-string parameters.

  • data (Mapping[str, Any] | None) – Form-urlencoded body for POST.

  • json_body (Any) – JSON body. Mutually exclusive with data.

  • extra_headers (Mapping[str, str] | None) – Per-request headers; merged on top of HttpClientConfig.default_headers.

  • allow_redirects (bool) – Follow 3xx redirects.

Returns:

tls_client.response.Response for any 2xx / 3xx status.

Raises:
Return type:

Response

class ogu_api.HttpClientConfig(base_url='https://oguser.com', timeout_seconds=30.0, max_retries=0, retry_backoff_seconds=0.5, client_identifier='chrome131', default_headers=<factory>)[source]

Bases: object

Frozen configuration for HttpClient.

Parameters:
base_url

Forum base URL, default https://oguser.com.

Type:

str

timeout_seconds

Per-request timeout.

Type:

float

max_retries

How many times to retry on 429 and 5xx (default 0 — no retries).

Type:

int

retry_backoff_seconds

Base delay for exponential backoff between retries.

Type:

float

client_identifier

TLS fingerprint identifier passed to tls_client.Session (default 'chrome131').

Type:

str

default_headers

Per-request default headers. Merged with per-request extra_headers; per-request values win.

Type:

Mapping[str, str]

class ogu_api.Proxy(proxy=None)[source]

Bases: object

Normalize proxy strings to the http://... form tls_client expects.

Accepts:

  • user:pass@host:port (standard URL form)

  • host:port:user:pass (proxy-list form)

  • host:port (no auth)

Existing scheme prefixes (http://, socks5://, etc.) are stripped before matching; the result is always http://....

Parameters:

proxy (str | None) – Raw proxy string in any of the accepted forms. None to disable.

proxy

Normalized proxy URL, or None when no input was provided or the input couldn’t be parsed.

Example

>>> Proxy('user:pass@host:8080').proxy
'http://user:pass@host:8080'
>>> Proxy('host:8080:user:pass').proxy
'http://user:pass@host:8080'
>>> Proxy('host:8080').proxy
'http://host:8080'

Models

class ogu_api.UserProfile(user_id, username=None, reputation=0, vouches=0, credits=0)[source]

Bases: object

Parsed forum profile.

Returned by UsersResource.get_by_username() and UsersResource.get_by_id(). Numeric fields default to 0 when the profile page doesn’t render them (e.g. brand-new accounts).

Parameters:
  • user_id (int)

  • username (str | None)

  • reputation (int)

  • vouches (int)

  • credits (int)

user_id

Numeric user id (the value shown in the #profileLink anchor).

Type:

int

username

Canonical-cased username.

Type:

str | None

reputation

Positive reputation count.

Type:

int

vouches

Vouch count.

Type:

int

credits

Current credits balance.

Type:

int

class ogu_api.Inbox(messages=(), conversation_ids=(), my_post_key=None)[source]

Bases: object

Parsed inbox page.

Returned by MessagesResource.inbox(). my_post_key is the form CSRF token used by every state-changing call across the SDK; you can cache it from a single inbox fetch and reuse across many sends.

Parameters:
messages

Inbox rows (newest first).

Type:

tuple[ogu_api.models.Message, …]

conversation_ids

Opaque conversation ids deduped from anchor hrefs.

Type:

tuple[str, …]

my_post_key

CSRF token from the page; None if the inbox failed to render (e.g. session expired).

Type:

str | None

class ogu_api.Message(username, date, message)[source]

Bases: object

A row in the PM inbox table.

Parameters:
username

Other party’s username.

Type:

str

date

UTC unix timestamp of the message; 0 if the date column couldn’t be parsed.

Type:

int

message

Message preview text.

Type:

str

class ogu_api.ThreadSummary(title, link, tid=None)[source]

Bases: object

A thread reference extracted from a feed / forum / search page.

Parameters:
title

Visible thread title.

Type:

str

URL path. Either /showthread.php?tid=N&... (numeric) or /Thread-Slug-... (rewritten). Pass to ThreadsResource.get_by_link() to fetch.

Type:

str

tid

Numeric thread id when one is recoverable from the link or from a sibling anchor in the same row. None for explore slug-only entries.

Type:

int | None

class ogu_api.Thread(tid, title, posts, page=1, total_pages=1)[source]

Bases: object

A parsed thread page.

Returned by ThreadsResource.read(). Multi-page threads can be fetched one page at a time; page and total_pages reflect the current view.

Parameters:
tid

Thread id.

Type:

int

title

Thread subject (best-effort: from the page <title> tag, stripped of the trailing " | OGU" suffix).

Type:

str

posts

Posts on the current page, in display order.

Type:

tuple[ogu_api.models.Post, …]

page

1-indexed current page number.

Type:

int

total_pages

Total page count when discoverable, else 1.

Type:

int

class ogu_api.Post(pid, author, author_id, body, date, date_label='', signature='')[source]

Bases: object

A single post inside a thread.

Parameters:
  • pid (int)

  • author (str | None)

  • author_id (int | None)

  • body (str)

  • date (int)

  • date_label (str)

  • signature (str)

pid

Post id.

Type:

int

author

Username of the poster.

Type:

str | None

author_id

Numeric user id of the poster, or None if it couldn’t be recovered from the post header.

Type:

int | None

body

Post body text only (signature stripped out, HTML reduced to plain text).

Type:

str

date

UTC unix timestamp of the post; 0 on parse failure.

Type:

int

date_label

Original date string from the page ('Yesterday', '2 hours ago', '05-06-2026, 04:23 PM' etc.) for display.

Type:

str

signature

The poster’s signature text (empty if none).

Type:

str

class ogu_api.RecentTransaction(sender, recipient, amount, date)[source]

Bases: object

A row in the public /credits.php?action=stats recently-sent table.

Parameters:
sender

Donor username.

Type:

str

recipient

Recipient username.

Type:

str

amount

Credits transferred.

Type:

int

date

UTC unix timestamp of the transaction; 0 on parse failure.

Type:

int

class ogu_api.ReputationPage(values=(), hidden=<factory>)[source]

Bases: object

Parsed reputation modal for a target user.

Parameters:
values

Allowed reputation values, e.g. ('-1', '0', '1'). Empty when the current account can’t rep this target (yourself, or a cooldown applies).

Type:

tuple[str, …]

hidden

Hidden form fields needed to POST a reputation.

Type:

dict[str, Any]

class ogu_api.ActionResult(success, message='')[source]

Bases: object

Generic result for state-changing operations.

Currently only used by helpers in user code. Most resource methods return raw responses so callers can branch on body content.

Parameters:

Resources

class ogu_api.resources.SessionResource(http_client)[source]

Bases: ResourceBase

Login, logout, and the public landing page.

Wraps /member.php?action=login (form + submit) and /member.php?action=logout. The session cookie (ogusid) ends up on the client’s cookie jar after a successful login.

Parameters:

http_client (HttpClient)

async get_home()[source]

Fetch the forum landing page (/index.php).

Returns:

Raw response. Useful for sanity-checking the session (len(response.text) should be substantial when logged in).

Return type:

Response

async get_login_page()[source]

Fetch the login form page (/member.php?action=login).

Returns:

Raw response. Pass response.text to extract_login_hidden() to recover the CSRF / hidden fields when calling login() manually.

Return type:

Response

async login(username, password, two_factor='', *, hidden=None)[source]

Submit the login form and capture the session cookie.

When hidden is omitted, the SDK first GETs /member.php?action=login and extracts the hidden fields itself, so most callers just pass username + password.

Parameters:
  • username (str) – Forum username.

  • password (str) – Forum password.

  • two_factor (str) – Current TOTP code. Empty string if 2FA is disabled.

  • hidden (Mapping[str, Any] | None) – Pre-fetched hidden form fields. Skips the extra round-trip.

Returns:

Raw POST response. The ogusid session cookie is set on client.cookies automatically on success.

Return type:

Response

Example

>>> await client.session.login('username', 'password')
>>> await client.session.login('username', 'password', '123456')
async logout(logout_key=None)[source]

End the current session.

OGU’s logout requires a logoutkey query parameter. When omitted, the SDK fetches the home page and extracts it from the logout link.

Parameters:

logout_key (str | None) – Pre-fetched key. Skips the extra round-trip when provided.

Returns:

Raw GET response. The session cookies are invalidated server-side.

Return type:

Response

static extract_login_hidden(page_html)[source]

Pull the hidden <input> fields off the login form.

Parameters:

page_html (str) – HTML of /member.php?action=login.

Returns:

{name: value} for every hidden input inside <form action="member.php">.

Return type:

dict[str, Any]

class ogu_api.resources.UsersResource(http_client)[source]

Bases: ResourceBase

Profile lookup by username or numeric user id.

The high-level get_by_* methods return a UserProfile dataclass with parsed integer fields. The get_page_* variants return the raw tls_client.response.Response so callers can feed the HTML to the static extract_* parsers themselves.

Parameters:

http_client (HttpClient)

async get_by_username(username)[source]

Fetch a user’s profile by username.

Hits the slug-rewritten URL /{username}.

Parameters:

username (str) – Forum username (case-insensitive).

Returns:

Parsed UserProfile.

Raises:
  • OGUParseError – The page rendered, but no user_id could be extracted (usually a banned, deleted, or non-existent user).

  • OGUNotFoundError – Profile page returned 404.

Return type:

UserProfile

Example

>>> user = await client.users.get_by_username('forgivenforget')
>>> print(user.user_id, user.credits, user.reputation)
async get_by_id(user_id)[source]

Fetch a user’s profile by numeric user id.

Hits /member.php?action=profile&uid={user_id}.

Parameters:

user_id (str | int) – Numeric user id (the value shown by extract_user_id()).

Returns:

Parsed UserProfile.

Raises:

OGUParseError – The page rendered, but no user_id could be extracted.

Return type:

UserProfile

async get_page_by_username(username)[source]

Fetch the raw profile-page response by username (no parsing).

Parameters:

username (str)

Return type:

Response

async get_page_by_id(user_id)[source]

Fetch the raw profile-page response by uid (no parsing).

Parameters:

user_id (str | int)

Return type:

Response

static extract_user_id(page_html)[source]

Pull the numeric user id from a profile page (text inside #profileLink).

Parameters:

page_html (str)

Return type:

str | None

static extract_username(page_html)[source]

Pull the canonical-cased username from <span class="usernamefont">.

Parameters:

page_html (str)

Return type:

str | None

static extract_reputation(page_html)[source]

Pull the positive-reputation count off a profile page ('0' if absent).

Parameters:

page_html (str)

Return type:

str

static extract_vouches(page_html)[source]

Pull the vouch count from the profile sidebar ('0' if absent).

Parameters:

page_html (str)

Return type:

str

static extract_credits(page_html)[source]

Pull the credits balance off a profile page ('0' if absent).

Parameters:

page_html (str)

Return type:

str

class ogu_api.resources.UserCPResource(http_client)[source]

Bases: ResourceBase

User Control Panel: account settings, profile, signature, credentials.

Every update_* / change_* method auto-fetches its own my_post_key from the corresponding form page when not provided. POSTs land on /usercp.php with the appropriate action=do_* field.

Parameters:

http_client (HttpClient)

async get_overview()[source]

Fetch the UserCP landing page (/usercp.php).

Return type:

Response

async get_options_page()[source]

Fetch the options form page.

Return type:

Response

async get_profile_page()[source]

Fetch the profile-edit form page.

Return type:

Response

async get_signature_page()[source]

Fetch the signature-edit form page.

Return type:

Response

async get_change_username_page()[source]

Fetch the change-username form page.

Return type:

Response

async get_change_password_page()[source]

Fetch the change-password form page.

Return type:

Response

async get_change_email_page()[source]

Fetch the change-email form page.

Return type:

Response

async update_notepad(notepad, *, my_post_key=None)[source]

Save the UserCP notepad scratch text.

Parameters:
  • notepad (str) – New notepad contents.

  • my_post_key (str | None) – Pre-fetched CSRF token. Auto-fetched from /usercp.php when omitted.

Return type:

Response

async update_signature(signature, *, update_existing_posts='0', my_post_key=None)[source]

Update the forum signature.

Parameters:
  • signature (str) – New signature body (BBCode supported).

  • update_existing_posts (str) – 'enable', 'disable', or '0'. Whether to re-render the new signature on already-posted messages.

  • my_post_key (str | None) – Pre-fetched CSRF token.

Return type:

Response

async update_options(options, *, my_post_key=None)[source]

Update the UserCP options form (notification prefs, theme, etc.).

Parameters:
  • options (Mapping[str, Any]) – Mapping of form fields to values, e.g. {'allownotices': '1', 'receivepms': '1', 'tpp': '20'}. Field names mirror the page’s <input name=...> attributes — fetch get_options_page() once to enumerate them.

  • my_post_key (str | None) – Pre-fetched CSRF token.

Return type:

Response

async update_profile(fields, *, my_post_key=None)[source]

Update the profile-edit form (bio, custom fields, user title, …).

Parameters:
  • fields (Mapping[str, Any]) – Mapping of profile field names to values, e.g. {'usertitle': 'foo', 'profile_fields[fid5]': 'bar'}.

  • my_post_key (str | None) – Pre-fetched CSRF token.

Return type:

Response

async change_username(new_username, password, *, my_post_key=None)[source]

Change your forum username (subject to OGU’s cooldown rules).

Parameters:
  • new_username (str) – New username.

  • password (str) – Current password (required by OGU for confirmation).

  • my_post_key (str | None) – Pre-fetched CSRF token.

Return type:

Response

async change_password(old_password, new_password, *, my_post_key=None)[source]

Change your forum password.

Parameters:
  • old_password (str) – Current password.

  • new_password (str) – New password (sent twice as password and password2).

  • my_post_key (str | None) – Pre-fetched CSRF token.

Return type:

Response

async change_email(new_email, password, *, my_post_key=None)[source]

Change your account email.

Parameters:
  • new_email (str) – New email address (sent twice as email and email2).

  • password (str) – Current password (required by OGU for confirmation).

  • my_post_key (str | None) – Pre-fetched CSRF token.

Return type:

Response

class ogu_api.resources.ReputationResource(http_client)[source]

Bases: ResourceBase

Send reputation to a user.

Paths:

  • /reputation.php?action=add&uid={uid}&pid=0&modal=1 — modal form.

  • /reputation.php?modal=1 (POST) — submit.

Parameters:

http_client (HttpClient)

async get_page_response(user_id)[source]

Fetch the raw reputation modal for a target user (no parsing).

Parameters:

user_id (str | int)

Return type:

Response

async get_page(user_id)[source]

Fetch the reputation modal and return the parsed form data.

Parameters:

user_id (str | int) – Target user id.

Returns:

ReputationPage with values (allowed reputation amounts as strings, e.g. ('-1', '0', '1')) and hidden (CSRF / form fields).

Return type:

ReputationPage

Note

values is empty when the target won’t accept reputation from the current account (e.g. yourself, or someone you’ve already repped recently).

async send(reputation, comment, uid='', *, hidden=None, validate=True)[source]

Submit a reputation rating.

When hidden is omitted, the SDK first loads the rep modal for uid to recover the form fields. With validate=True (default), it also checks that reputation appears in the modal’s allowed <select id="reputation"> options before submitting.

Parameters:
  • reputation (int) – -1, 0, or 1 — depending on what the target’s modal allows.

  • comment (str) – Comment attached to the rating.

  • uid (str | int) – Target user id.

  • hidden (Mapping[str, Any] | None) – Pre-fetched hidden form fields.

  • validate (bool) – Validate reputation against the modal’s allowed values before posting. Set to False to skip the GET.

Returns:

Raw POST response. The body containing "Your reputation rating has successfully been added" indicates success.

Raises:

OGUReputationErrorvalidate=True and reputation is not in the target’s allowed values.

Return type:

Response

Example

>>> await client.reputation.send(
...     reputation = 1,
...     comment = 'good trade',
...     uid = 12345,
... )
static extract_values(page_html)[source]

Pull the allowed reputation values from <select id="reputation">.

Parameters:

page_html (str)

Return type:

list[str]

static extract_hidden(page_html)[source]

Pull hidden inputs off the reputation form (form[action="reputation.php"]).

Parameters:

page_html (str)

Return type:

dict[str, Any]

class ogu_api.resources.CreditsResource(http_client)[source]

Bases: ResourceBase

OGU credits: donate, send, and the public stats feed.

Paths:

  • /credits.php?action=donate — donate form.

  • /credits.php?action=stats — recently-sent stats.

  • /credits.php (POST) — submit donation. Requires a solved hCaptcha token; this SDK does not ship a solver.

Parameters:

http_client (HttpClient)

async get_donate_page()[source]

Fetch the donate form page.

Return type:

Response

async get_stats_page()[source]

Fetch the credits stats page (raw HTML; recent transactions table).

Return type:

Response

async recently_sent()[source]

Parse the public “recently sent” feed.

Returns:

Newest-first list of RecentTransaction. date is a UTC unix timestamp; 0 if the cell couldn’t be parsed.

Return type:

list[RecentTransaction]

Example

>>> for tx in await client.credits.recently_sent():
...     print(f'{tx.sender} -> {tx.recipient}: {tx.amount}')
async send(username, amount, captcha_token, reason='', *, hidden=None)[source]

Send credits to another user.

OGU requires a solved hCaptcha token on every donation; you must provide one. The hidden form fields (donate_token and friends) are auto-fetched when hidden is omitted.

Parameters:
  • username (str) – Recipient username.

  • amount (int | float) – Number of credits to send.

  • captcha_token (str) – A solved hCaptcha token, sent as both g-recaptcha-response and h-captcha-response.

  • reason (str) – Optional message attached to the donation.

  • hidden (Mapping[str, Any] | None) – Pre-fetched hidden form fields. Skips the auto-fetch round-trip.

Returns:

Raw POST response. "You may close this page." in the body indicates success.

Return type:

Response

Example

>>> await client.credits.send(
...     username = 'recipient',
...     amount = 100,
...     captcha_token = solved_token,
... )
static extract_hidden(page_html)[source]

Pull hidden inputs off the donate form (form[action="credits.php"]).

Parameters:

page_html (str)

Return type:

dict[str, Any]

static extract_recently_sent(page_html)[source]

Parse the recently-sent transactions table.

Parameters:

page_html (str) – HTML of /credits.php?action=stats.

Returns:

Newest-first list of RecentTransaction.

Return type:

list[RecentTransaction]

class ogu_api.resources.MessagesResource(http_client)[source]

Bases: ResourceBase

Private messages: inbox, conversations, compose, send, delete.

Maps to /private.php and (the alias) /messages. Paths used:

  • /private.php — inbox listing.

  • /private.php?action=send — compose form.

  • /private.php?action=read&convid={id} — single conversation.

  • /private.php?action=tracking — tracking (sent / read receipts).

  • /private.php (POST, action=do_send) — send.

  • /private.php (POST, action=do_stuff) — bulk delete.

Parameters:

http_client (HttpClient)

async get_inbox(*, page=None)[source]

Fetch the raw inbox page.

Parameters:

page (int | None) – 1-indexed pagination page. None for the first page.

Returns:

Raw response. Pass response.text to extract_messages() and extract_conversation_ids(), or use inbox() to get an Inbox dataclass directly.

Return type:

Response

async inbox(*, page=None)[source]

Fetch and parse the inbox page.

Parameters:

page (int | None) – 1-indexed pagination page.

Returns:

Inbox with messages, conversation_ids, and my_post_key. my_post_key is the form CSRF token used by every state-changing call across the SDK; you can pass it through to subsequent send / delete calls to skip an extra round-trip.

Return type:

Inbox

Example

>>> inbox = await client.messages.inbox()
>>> for m in inbox.messages:
...     print(m.username, m.message)
async get_compose_page()[source]

Fetch the compose form (/private.php?action=send).

Return type:

Response

async get_conversation(conversation_id)[source]

Fetch a single conversation thread by id.

Parameters:

conversation_id (str) – The opaque convid from extract_conversation_ids() or Inbox.conversation_ids.

Return type:

Response

async get_tracking()[source]

Fetch the message-tracking page (sent / read receipts).

Return type:

Response

async send(to, message, *, my_post_key=None, hidden=None)[source]

Send a private message.

When my_post_key and hidden are omitted, the SDK first GETs the compose page to recover them, then POSTs the message.

Parameters:
  • to (str) – Recipient username.

  • message (str) – Message body.

  • my_post_key (str | None) – Pre-fetched CSRF token. Skips the auto-fetch round-trip.

  • hidden (Mapping[str, Any] | None) – Pre-fetched hidden form fields. Skips the auto-fetch round-trip.

Returns:

Raw POST response. A 200 with the inbox layout indicates success.

Return type:

Response

Example

>>> await client.messages.send(to = 'recipient', message = 'hello')
>>> # Reuse the key when sending many at once
>>> inbox = await client.messages.inbox()
>>> for r in recipients:
...     await client.messages.send(to = r, message = 'hi', my_post_key = inbox.my_post_key)
async delete(message_hashes, *, my_post_key=None)[source]

Bulk-delete messages by hash.

Each row in the inbox table has a toDelete[<hash>] checkbox; the hash is an opaque per-message id that you’d extract from the inbox page yourself if you need fine control.

Parameters:
  • message_hashes (Iterable[str]) – Iterable of message hashes to delete.

  • my_post_key (str | None) – Pre-fetched CSRF token. Auto-fetched from the inbox page when omitted.

Returns:

Raw POST response.

Return type:

Response

static extract_compose_hidden(page_html)[source]

Pull hidden inputs off the compose form (form[action="private.php"]).

Parameters:

page_html (str)

Return type:

dict[str, Any]

static extract_messages(page_html)[source]

Parse the inbox table rows into Message items.

Returns:

One Message per visible inbox row, in the order the page renders them (newest first). date is a UTC unix timestamp; 0 if the date column couldn’t be parsed.

Parameters:

page_html (str)

Return type:

list[Message]

static extract_conversation_ids(page_html)[source]

Walk anchors with convid= and return the deduped list of ids.

Parameters:

page_html (str)

Return type:

list[str]

class ogu_api.resources.NotificationsResource(http_client)[source]

Bases: ResourceBase

Site notifications and the alerts dropdown.

Paths:

  • /notifications — full notifications page.

  • /alerts.php — alerts page.

  • /alerts.php?action=read — mark all alerts read.

Parameters:

http_client (HttpClient)

async get_notifications()[source]

Fetch the notifications page.

Return type:

Response

async get_alerts()[source]

Fetch the alerts page.

Return type:

Response

async mark_alerts_read()[source]

Mark every alert as read (/alerts.php?action=read).

Return type:

Response

static extract_alert_count(page_html)[source]

Best-effort extraction of the unread-alert count from any page.

Looks for <span class="alerts-count"> first, then falls back to a data-alerts-count attribute.

Returns:

Unread count, or 0 if the badge couldn’t be located.

Parameters:

page_html (str)

Return type:

int

class ogu_api.resources.FeedResource(http_client)[source]

Bases: ResourceBase

Forum landing pages: explore (marketplace) and home.

Paths:

  • /explore.php — marketplace landing (slug-rewritten URLs).

  • /index.php — home / activity feed (numeric tid URLs).

Most callers want explore() and home() (parsed ThreadSummary lists). The get_* and extract_* methods are escape hatches.

Parameters:

http_client (HttpClient)

async get_explore()[source]

Fetch the raw explore page.

Return type:

Response

async get_home()[source]

Fetch the raw home page.

Return type:

Response

async explore()[source]

Parsed explore feed.

Returns:

List of ThreadSummary. link is the slug-rewritten path (/Thread-...). tid is None for entries whose only link form is slug-only.

Return type:

list[ThreadSummary]

async home()[source]

Parsed home feed.

Returns:

List of ThreadSummary for every thread on /index.php. The “last post” jump-link in each row is merged with the title link, so each thread appears exactly once.

Return type:

list[ThreadSummary]

static extract_thread_summaries(page_html)[source]

Walk every thread anchor in the page and collapse rows into summaries.

Title links (/Thread-Slug) and “last post” jump-links (/showthread.php?tid=N&action=lastpost) for the same thread are merged into one ThreadSummary by looking for a sibling tid anchor in the same <tr> row when the current anchor has no tid of its own.

Parameters:

page_html (str) – HTML of any page containing thread links — explore, home, /forumdisplay.php?fid=..., search results, etc.

Returns:

One ThreadSummary per unique thread, in document order.

Return type:

list[ThreadSummary]

Return deduped thread URL paths in document order.

Catches both /showthread.php?tid=N and /Thread-Slug forms. Use this when you don’t need titles — extract_thread_summaries() gives you titles + tid for free.

Parameters:

page_html (str)

Return type:

list[str]

static extract_thread_ids(page_html)[source]

Numeric thread ids only (skips slug-rewritten links).

Parameters:

page_html (str)

Return type:

list[str]

class ogu_api.resources.ThreadsResource(http_client)[source]

Bases: ResourceBase

View threads / forums and post replies and new threads.

Paths:

  • /showthread.php?tid={tid} — read a thread.

  • /forumdisplay.php?fid={fid} — read a forum’s thread list.

  • /newreply.php?tid={tid} — reply form.

  • /newreply.php?tid={tid}&processed=1 (POST) — submit reply.

  • /newthread.php?fid={fid} — new-thread form.

  • /newthread.php?fid={fid}&processed=1 (POST) — submit new thread.

Parameters:

http_client (HttpClient)

async get(thread_id, *, page=None)[source]

Fetch a thread by numeric id.

Parameters:
  • thread_id (str | int) – Numeric tid.

  • page (int | None) – 1-indexed page within the thread (newer pages of replies).

Return type:

Response

async read(thread_id, *, page=None)[source]

Fetch and parse a thread page into a Thread dataclass.

Each post becomes a Post with pid, author, author_id, body, date (UTC unix timestamp), and the original date_label from the page.

Parameters:
  • thread_id (str | int) – Numeric tid.

  • page (int | None) – 1-indexed page within the thread.

Returns:

Parsed Thread.

Return type:

Thread

Example

>>> thread = await client.threads.read(1286827)
>>> print(thread.title, thread.page, thread.total_pages)
>>> for post in thread.posts:
...     print(post.author, post.date_label, post.body[:80])

Fetch a thread by an arbitrary link (numeric or slug-rewritten).

Parameters:

link (str) – Path returned by FeedResource.extract_thread_links(), e.g. /Thread-Card-to-Crypto or /showthread.php?tid=123&action=lastpost.

Return type:

Response

async get_lastpost(thread_id)[source]

Jump to the last post of a thread (?action=lastpost).

Parameters:

thread_id (str | int)

Return type:

Response

async get_forum(forum_id, *, page=None)[source]

Fetch a forum’s thread listing.

Parameters:
  • forum_id (str | int) – Numeric fid.

  • page (int | None) – 1-indexed page of the listing.

Return type:

Response

async get_reply_page(thread_id)[source]

Fetch the reply form for a thread (raw HTML).

Parameters:

thread_id (str | int)

Return type:

Response

async reply(thread_id, message, *, my_post_key=None, hidden=None)[source]

Post a reply to a thread.

Auto-fetches the reply form when hidden is omitted, so the common case is just await client.threads.reply(tid, message).

Parameters:
  • thread_id (str | int) – Numeric tid.

  • message (str) – Reply body (BBCode supported by the forum).

  • my_post_key (str | None) – Pre-fetched CSRF token. Auto-fetched from the reply form if omitted.

  • hidden (Mapping[str, Any] | None) – Pre-fetched hidden form fields. Includes posthash, subject, etc.

Returns:

Raw POST response.

Return type:

Response

Example

>>> await client.threads.reply(1286827, 'great post')
async get_new_thread_page(forum_id)[source]

Fetch the new-thread form for a forum (raw HTML).

Parameters:

forum_id (str | int)

Return type:

Response

async create(forum_id, subject, message, *, my_post_key=None, hidden=None)[source]

Create a new thread in a forum.

Parameters:
  • forum_id (str | int) – Numeric fid of the destination forum.

  • subject (str) – Thread title.

  • message (str) – Thread body.

  • my_post_key (str | None) – Pre-fetched CSRF token.

  • hidden (Mapping[str, Any] | None) – Pre-fetched hidden form fields.

Returns:

Raw POST response.

Return type:

Response

Example

>>> await client.threads.create(13, subject = 'hi', message = 'first post')
classmethod parse(page_html, *, thread_id, page=1)[source]

Parse a /showthread.php page into a Thread.

Parameters:
  • page_html (str) – Raw HTML body of a thread page.

  • thread_id (int) – Numeric tid (used as the Thread.tid field).

  • page (int) – 1-indexed page number (used as the Thread.page field).

Returns:

Parsed Thread.

Return type:

Thread

static extract_reply_hidden(page_html)[source]

Pull hidden inputs off the reply form (form[action*="newreply.php"]).

Parameters:

page_html (str)

Return type:

dict[str, Any]

static extract_new_thread_hidden(page_html)[source]

Pull hidden inputs off the new-thread form.

Parameters:

page_html (str)

Return type:

dict[str, Any]

class ogu_api.resources.SearchResource(http_client)[source]

Bases: ResourceBase

Forum-wide search and the lookup form.

Paths:

  • /search.php (GET) — search form.

  • /search.php (POST, action=do_search) — submit search.

  • /search.php?action=results&sid={id} — paginated results page.

  • /lookup.php — quick username lookup form.

Parameters:

http_client (HttpClient)

async get_form()[source]

Fetch the search form page (raw HTML).

Return type:

Response

async search(keywords, *, author='', forums=None, find_thread_starters_only=False, match_username_exactly=False, post_or_thread='thread', post_date='', post_date_direction='older', sort_by='lastpost', sort_direction='desc', show_results='threads')[source]

Submit a search query.

Parameters:
  • keywords (str) – Search keywords.

  • author (str) – Filter by post author.

  • forums (list[str] | None) – List of forum ids (fid) to scope to.

  • find_thread_starters_only (bool) – Only match the first post of each thread (i.e. thread starters).

  • match_username_exactly (bool) – Treat author as an exact match instead of substring.

  • post_or_thread (str) – 'thread' or 'post'.

  • post_date (str) – Date filter (e.g. '7' for last 7 days).

  • post_date_direction (str) – 'older' or 'newer'.

  • sort_by (str) – 'lastpost', 'subject', 'replies', etc.

  • sort_direction (str) – 'asc' or 'desc'.

  • show_results (str) – 'threads' or 'posts'.

Returns:

Raw POST response — usually a redirect to /search.php?action=results&sid=....

Return type:

Response

async get_results(search_id)[source]

Fetch the results page for a previous search by sid.

Parameters:

search_id (str)

Return type:

Response

async lookup(*, forum_1='', forum_5='')[source]

Hit the /lookup.php quick lookup form.

Parameters:
  • forum_1 (str) – Value for the fid1 field.

  • forum_5 (str) – Value for the fid5 field.

Return type:

Response

class ogu_api.resources.MembersResource(http_client)[source]

Bases: ResourceBase

Member browsing: list, top, team, groups, vouches, awards, rep history.

All methods return the raw page response — parse the HTML yourself with BeautifulSoup or feed it to the parsers in ogu_api.resources.users when you only need profile fields.

Parameters:

http_client (HttpClient)

async list(*, username='', username_match='contains', sort='username', order='asc', page=None, per_page=None)[source]

Fetch the paginated member list.

Parameters:
  • username (str) – Filter by username substring (or exact, see username_match).

  • username_match (str) – 'contains', 'begins', 'ends', or 'exact'.

  • sort (str) – Sort key — 'username', 'regdate', 'lastvisit', 'postnum'.

  • order (str) – 'asc' or 'desc'.

  • page (int | None) – 1-indexed pagination page.

  • per_page (int | None) – Members per page.

Return type:

Response

async top()[source]

Fetch the top-members page (/top.php).

Return type:

Response

async team()[source]

Fetch the staff / team page (/showteam.php).

Return type:

Response

async statistics()[source]

Fetch the forum statistics page (/statistics.php).

Return type:

Response

async groups()[source]

Fetch the official groups listing (/groups.php).

Return type:

Response

async vouches(user_id)[source]

Fetch a user’s vouches page (/vouches.php?id={uid}).

Parameters:

user_id (str | int)

Return type:

Response

async awards()[source]

Fetch the awards index (/awards.php).

Return type:

Response

async awards_for_user(user_id)[source]

Fetch a single user’s awards page (/awards.php?uid={uid}).

Parameters:

user_id (str | int)

Return type:

Response

async reputation_history(user_id)[source]

Fetch a user’s reputation history page (/reputation.php?uid={uid}).

Parameters:

user_id (str | int)

Return type:

Response

Errors

exception ogu_api.errors.OGUError[source]

Bases: Exception

Base class for every exception raised by ogu-api.

Catch this when you want to handle anything the SDK might throw.

exception ogu_api.errors.OGUAPIError(message, *, status_code, method, url, body=None)[source]

Bases: OGUError

HTTP error response from the forum (4xx / 5xx).

Subclassed by status-code-specific errors (OGUNotFoundError, etc.). The exact subclass is chosen by HttpClient based on the status.

Parameters:
Return type:

None

status_code

HTTP status code.

method

HTTP method used ('GET', 'POST', …).

url

Full URL of the failing request.

body

Response body, if available, for inspection.

exception ogu_api.errors.OGUAuthenticationError(message, *, status_code, method, url, body=None)[source]

Bases: OGUAPIError

HTTP 401: missing or invalid session cookie.

Parameters:
Return type:

None

exception ogu_api.errors.OGUAuthorizationError(message, *, status_code, method, url, body=None)[source]

Bases: OGUAPIError

HTTP 403: session is valid but the action isn’t permitted.

Parameters:
Return type:

None

exception ogu_api.errors.OGUNotFoundError(message, *, status_code, method, url, body=None)[source]

Bases: OGUAPIError

HTTP 404: resource (profile, thread, page) not found.

Parameters:
Return type:

None

exception ogu_api.errors.OGUValidationError(message, *, status_code, method, url, body=None)[source]

Bases: OGUAPIError

HTTP 400 / 422: form or query parameters were rejected.

Parameters:
Return type:

None

exception ogu_api.errors.OGURateLimitError(message, *, status_code, method, url, body=None, retry_after_seconds=None)[source]

Bases: OGUAPIError

HTTP 429: rate-limited by the forum or fronting CDN.

Parameters:
  • message (str)

  • status_code (int)

  • method (str)

  • url (str)

  • body (str | None)

  • retry_after_seconds (float | None)

Return type:

None

retry_after_seconds

Retry-After header value when present, else None. The HTTP layer honors this automatically when retries are configured.

exception ogu_api.errors.OGUServerError(message, *, status_code, method, url, body=None)[source]

Bases: OGUAPIError

HTTP 5xx: forum or upstream returned a server error.

Parameters:
Return type:

None

exception ogu_api.errors.OGUNetworkError[source]

Bases: OGUError

Network-level failure: connection refused, DNS error, TLS handshake, etc.

exception ogu_api.errors.OGUTimeoutError[source]

Bases: OGUNetworkError

Request exceeded HttpClientConfig.timeout_seconds.

exception ogu_api.errors.OGUParseError(message='Failed to parse response')[source]

Bases: OGUError

A page came back, but expected HTML structure was missing.

Raised when an extractor needed a specific element that wasn’t present — e.g. profile pages without #profileLink.

Parameters:

message (str)

Return type:

None

exception ogu_api.errors.OGUSessionError(message='Session is invalid or expired')[source]

Bases: OGUError

The session cookie is missing or has expired.

Raised by helpers that depend on a logged-in session.

Parameters:

message (str)

Return type:

None

exception ogu_api.errors.OGULoginError(message='Login failed')[source]

Bases: OGUError

Login flow returned an error indicator from the forum.

Parameters:

message (str)

Return type:

None

exception ogu_api.errors.OGUReputationError(message='Reputation request failed', *, valid_amounts=None)[source]

Bases: OGUError

The forum rejected a reputation submission.

Most commonly raised when the value passed to ReputationResource.send() isn’t in the target user’s allowed options.

Parameters:
Return type:

None

valid_amounts

List of allowed reputation values returned by the target’s modal, or None when not available.

exception ogu_api.errors.OGUCreditsError(message='Credits request failed')[source]

Bases: OGUError

The forum rejected a credits transfer.

Parameters:

message (str)

Return type:

None