Getting Started¶
This guide walks through the typical pay-per-use workflow — the same loop a daily job-search notebook runs.
Prefer chat? Connect Resumly to a supported MCP client and run this same loop in plain English.
1. Install and Authenticate¶
from resumly import Resumly
client = Resumly(api_key="rly_your_api_key") # or set RESUMLY_API_KEY
client.balance() # {'balance_usd': 5.0, ...} — first key includes $5 trial credit
client.readiness() # what's missing before you can auto-apply
2. Upload Your Base Resume — $0.05¶
3. Build a Search from Plain English (free)¶
draft = client.interpret(
"remote senior data engineer roles in the US or Canada, posted in the last week"
)
print(draft["assumptions"]) # what the interpreter assumed
print(draft["draft_patch"]) # validated structured filters
# How many jobs would this shape match? Free, cached ~10 minutes.
print(client.supply_check(**draft["draft_patch"]))
4. Save the Search and Refresh It — $0.05 per refresh¶
q = client.create_query("Data Eng — remote", **draft["draft_patch"])
client.refresh(q["query_id"]) # 202 — jobs land already match-scored
# Later, decide whether another nickel is worth it:
for query in client.queries()["queries"]:
print(query["query_name"], query["last_refreshed_at"], query["new_jobs_last_run"])
5. Read Your Board (free)¶
jobs = client.jobs(min_match_score=0.7, auto_apply=True, sort_by="match")
for job in jobs["jobs"][:5]:
print(f"{job['match_score']:.0%} {job['job_title']} @ {job['organization']}")
best = jobs["jobs"][0]
client.save_job(best["job_id"])
# Or bring your own posting — $0.05:
imported = client.import_job("https://example.com/careers/senior-engineer")
6. Tailor a Resume — $0.25¶
resume = client.tailor(best["job_id"]) # for a board job
# resume = client.tailor(url="https://...") # or an outside URL
# resume = client.tailor(description="...") # or raw text
client.download(resume["resume_id"], path="tailored.docx") # free DOCX
print(client.comparison(resume["resume_id"])) # base vs tailored
7. Cover Letter and PDF — $0.10 + $0.02¶
client.cover_letter(resume["resume_id"])
op = client.export_pdf(resume["resume_id"], document_type="resume")
final = client.wait_operation(resume["resume_id"], op["operation_id"])
8. Interview Prep — $0.10 / $0.05 / $0.25¶
client.interview_questions(best["job_id"]) # $0.10
qs = client.get_interview_questions(best["job_id"])
feedback = client.interview_answer( # $0.05
best["job_id"], question_index=0,
answer="I led the migration of our monolith to microservices...",
)
client.company_research(best["job_id"]) # $0.25
9. Apply — $0.50, billed only on success¶
eligible = client.eligible_jobs(min_match_score=0.7)
queued = client.apply(best["job_id"], resume["resume_id"])
# $0.50 is reserved now and billed only when the submission is confirmed.
print(client.applications(status="applied"))
print(client.application_stats())
10. Autopilot and Inbox¶
# Hands-free: search, tailor and apply on a schedule (same per-op pricing)
client.enable_autopilot(job_queries=[q["query_id"]], min_match_score=70)
print(client.autopilot_dashboard())
# Employer replies land in your managed inbox, auto-classified
invites = client.inbox_emails(category="interview_invitation")
client.reply_email(invites["emails"][0]["id"], body_text="Thursday 2pm works great.")
Topping Up¶
url = client.buy_credit("25") # "10" | "25" | "50" | "100" — returns a Stripe checkout URL
print("Complete the top-up here:", url)
Error Handling¶
from resumly import (
ResumlyError, AuthenticationError, RateLimitError,
InsufficientCreditError, DuplicateRequestError,
)
try:
client.tailor(url="https://example.com/job")
except InsufficientCreditError as e:
print(f"Balance ${e.balance_usd} can't cover ${e.price_usd} — top up first")
except DuplicateRequestError:
print("Already ran with this idempotency key — not charged twice")
except RateLimitError as e:
print(f"Rate limited. Retry after {e.retry_after}s")
except ResumlyError as e:
print(f"API error: {e}")
Retries on metered POSTs are safe when you pass your own key: