Skip to main content

Python SDK for the LinWheel content engine API

Project description

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

linwheel-0.1.0.tar.gz (29.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

linwheel-0.1.0-py3-none-any.whl (7.7 kB view details)

Uploaded Python 3

File details

Details for the file linwheel-0.1.0.tar.gz.

File metadata

  • Download URL: linwheel-0.1.0.tar.gz
  • Upload date:
  • Size: 29.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for linwheel-0.1.0.tar.gz
Algorithm Hash digest
SHA256 831cdb0d54bb23e19d21544e347fb5dba9b0c2ae0c3f538a2fc9be9041ec9125
MD5 4af631fad65aae34c8b8a406c0410126
BLAKE2b-256 b4f6d1f68ac630dd99526b454c13aa3fcd137dca9a436cb433ed0238d2f1a5fa

See more details on using hashes here.

Provenance

The following attestation bundles were made for linwheel-0.1.0.tar.gz:

Publisher: publish-pypi.yml on Peleke/linwheel

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file linwheel-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: linwheel-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 7.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for linwheel-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d68516ec10fd8def93fcd608da7a714f51558a0fabbb8448fd358a2b0d10fe3d
MD5 7b7aa8563b20ec1e90f03d1359f56487
BLAKE2b-256 2208dde1972aa356e88de033efc075ed0e0ddb8d30c2c08c93e42ebf68b62d2a

See more details on using hashes here.

Provenance

The following attestation bundles were made for linwheel-0.1.0-py3-none-any.whl:

Publisher: publish-pypi.yml on Peleke/linwheel

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page