Skip to main content

linwheel

Python SDK for the LinWheel content engine API. Analyze, reshape, refine, and schedule LinkedIn content programmatically.

Built on httpx + pydantic. Sync client with full type hints.

Install

pip install linwheel

Quick Start

from linwheel import LinWheel

lw = LinWheel(api_key="lw_sk_...")

# Analyze content for LinkedIn potential
analysis = lw.analyze(text="Today I shipped a new SDK...")
# → { "linkedinFit": { "score": 8 }, "suggestedAngles": [...] }

# Reshape into angle-specific posts
result = lw.reshape(
    text="Today I shipped a new SDK...",
    angles=["field_note", "contrarian"],
    save_drafts=True,
)

# Refine a draft
result = lw.refine(text=result["posts"][0]["text"], intensity="medium")

# Approve and schedule
lw.posts.approve(post_id, approved=True)
lw.posts.schedule(post_id, scheduled_at="2026-02-24T09:00:00Z", auto_publish=True)

API Reference

Content Processing

# Analyze text for LinkedIn potential — topics, angles, fit score
analysis = lw.analyze(text="...", context="buildlog")

# Reshape content through 7 angle lenses
result = lw.reshape(
    text="...",
    angles=["field_note", "contrarian", "demystification"],
    pre_edit=False,        # light-edit input first
    instructions="...",    # custom instructions
    save_drafts=True,      # persist as drafts
)

# Refine with parameterized intensity
result = lw.refine(
    text="...",
    intensity="light",     # "light" | "medium" | "heavy"
    instructions="...",
    post_type="field_note",
    save_draft=True,
)

# Split long content into a post series
result = lw.split(
    text="...",
    max_posts=4,           # 2-10, default 5
    instructions="...",
    save_drafts=True,
)

Drafting

# Create a manual draft
draft = lw.draft(
    full_text="Your post content...",
    hook="Opening line",
    post_type="field_note",
    approved=False,
    auto_publish=True,
    scheduled_at="2026-02-24T09:00:00Z",
)

# Create a draft with image + carousel in one call
bundle = lw.bundle(
    full_text="Your post content...",
    image_headline_text="Big Title",
    image_style_preset="dark_mode",
    carousel_slides=[{"headlineText": "Slide 1"}, {"headlineText": "Slide 2"}],
)

Post Management

# List posts with filters
result = lw.posts.list(approved=False, limit=10)

# Get, update, approve, schedule
post = lw.posts.get(post_id)
lw.posts.update(post_id, full_text="Updated content...")
lw.posts.approve(post_id, approved=True)
lw.posts.schedule(post_id, scheduled_at="2026-02-24T09:00:00Z")

# Generate visuals
lw.posts.image(post_id, headline_text="...", style_preset="dark_mode")
lw.posts.carousel(
    post_id,
    slides=[{"headlineText": "Title"}, {"headlineText": "Content"}],
    style_preset="accent_bar",
)

Voice Profiles

Voice profiles inject your writing style into all content generation. Provide writing samples and LinWheel matches your voice.

# Create a voice profile from your writing
result = lw.voice_profiles.create(
    name="My Writing Voice",
    description="Technical, direct, slightly irreverent",
    samples=[article1, article2, article3],
    is_active=True,
)

# List all profiles
result = lw.voice_profiles.list()
# → { "profiles": [...], "activeProfileId": "..." }

# Switch active profile
lw.voice_profiles.activate(profile_id)

# Delete a profile
lw.voice_profiles.delete(profile_id)

Configuration

lw = LinWheel(
    api_key="lw_sk_...",                           # required
    base_url="https://www.linwheel.io",            # default
    signing_secret="your-hmac-secret",             # optional, for request signing
    timeout=60.0,                                  # default: 60s
)

# Context manager support
with LinWheel(api_key="lw_sk_...") as lw:
    lw.analyze(text="...")

HMAC Request Signing

If your API key has a signing secret, the SDK automatically signs every request with X-LW-Signature:

X-LW-Signature: t=<unix-seconds>,v1=<hmac-sha256-hex>

Canonical payload: <timestamp>.<METHOD>.<path>.<body-sha256>

Error Handling

from linwheel import LinWheel, LinWheelApiError, LinWheelAuthError

lw = LinWheel(api_key="lw_sk_...")

try:
    lw.analyze(text="...")
except LinWheelAuthError as e:
    # 401 — invalid or missing API key
    print(e, e.response_body)
except LinWheelApiError as e:
    # 4xx/5xx
    print(e.status_code, e, e.response_body)

The 7 Content Angles

Angle What it does
contrarian Challenge conventional wisdom
field_note Share a specific observation from experience
demystification Break down something complex
identity_validation Make the reader feel seen
provocateur Be deliberately provocative
synthesizer Connect dots across domains
curious_cat Ask genuine questions

Agent Example

The end-to-end flow from raw content to a scheduled LinkedIn post:

from linwheel import LinWheel

lw = LinWheel(api_key="lw_sk_...")

# 1. Feed in today's work
analysis = lw.analyze(text=daily_notes, context="buildlog")

# 2. Reshape through the best angles
top_angles = [a["angle"] for a in analysis["suggestedAngles"][:3]]
result = lw.reshape(text=daily_notes, angles=top_angles, save_drafts=True)

# 3. Refine the best draft
best = result["posts"][0]
refined = lw.refine(text=best["text"], intensity="medium", save_draft=True)

# 4. Approve and schedule for tomorrow 9am
lw.posts.approve(refined["postId"], approved=True)
lw.posts.schedule(refined["postId"], scheduled_at="2026-02-24T09:00:00Z", auto_publish=True)

License

MIT

Metadata

Release files for linwheel 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 linwheel 0.1.0
File Size Uploaded
linwheel-0.1.0.tar.gz 29.7 kB Details

Built distribution (wheel)

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

Total release size: 37.3 kB

Release files / linwheel-0.1.0.tar.gz

Download URL linwheel-0.1.0.tar.gz
Size 29.7 kB
Tags Source
SHA-256 checksum
How to use checksums
831cdb0d54bb23e19d21544e347fb5dba9b0c2ae0c3f538a2fc9be9041ec9125
BLAKE2b-256 checksum
How to use checksums
b4f6d1f68ac630dd99526b454c13aa3fcd137dca9a436cb433ed0238d2f1a5fa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

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 Feb 24, 2026.

Transparency log

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

Download URL linwheel-0.1.0-py3-none-any.whl
Size 7.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d68516ec10fd8def93fcd608da7a714f51558a0fabbb8448fd358a2b0d10fe3d
BLAKE2b-256 checksum
How to use checksums
2208dde1972aa356e88de033efc075ed0e0ddb8d30c2c08c93e42ebf68b62d2a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

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 Feb 24, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

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