
Generate introductions programmatically using the IntroMakerAI API, or connect IntroMakerAI to Claude and other AI assistants over MCP. to get an API key.
IntroMakerAI runs a remote MCP server, so any AI assistant that speaks MCP can write your introductions for you and save them straight to your library. Claude already knows the context of your conversation, so you can just ask for a bio and it fills in the details.
https://api.intromakerai.com/mcpTransport is streamable HTTP. Authenticate with the same API key as the REST API, sent as an Authorization header.
claude mcp add --transport http intromakerai https://api.intromakerai.com/mcp \
--header "Authorization: Bearer sk_your_api_key_here"Add this to your client's MCP configuration file.
{
"mcpServers": {
"intromakerai": {
"type": "http",
"url": "https://api.intromakerai.com/mcp",
"headers": {
"Authorization": "Bearer sk_your_api_key_here"
}
}
}
}| Tool | Cost | What it does |
|---|---|---|
| get_introduction_options | Free | The ways to start: your previous introductions, the platform templates, and the info on file |
| generate_introduction | 1 credit | Writes an introduction and saves it to your library |
| refine_introduction | 1 credit | Rewrites one you already have (shorter, more casual, custom instruction) |
| get_usage | Free | Credits left this period and the date they renew |
| save_introduction | Free | Saves text you already have to your library |
| list_introductions | Free | Lists your saved introductions, newest first |
| update_introduction | Free | Edits the text or title of a saved introduction |
| delete_introduction | Free | Permanently deletes a saved introduction |
Only generate_introduction and refine_introduction spend a credit, and they draw from the same plan allowance as the web app. Everything else is free, so asking Claude to fix a typo or reorder a line costs nothing.
Claude calls get_introduction_options first and asks you how you want to start, exactly like the website does. You get the same three choices, and they combine.
Saying "the same as my LinkedIn one but for Instagram" is enough. Claude picks the previous introduction and the Instagram template in one go, and the template wins on format so you keep your facts and get the right shape.
All API requests require a Bearer token in the Authorization header. You can create API keys from the Settings page.
Authorization: Bearer sk_your_api_key_hereGenerate a personalized introduction based on the provided input data. The endpoint accepts the same fields used in the app, so any template you can pick in the UI (LinkedIn headline, Twitter/X bio, Instagram bio, TikTok bio, Conference speaker, Podcast guest, Online community, Portfolio about) can be reproduced by sending the matching tone, types.Name, types.Audience, types["Purpose of the introduction"], types["Maximum length?"], and types.CTA values.
Or skip all of that and send a starting point instead: template for a platform preset, base_on_id to build on one of your saved introductions, and the personal info from your profile to fill the rest. Anything you send explicitly always wins.
| Field | Type | Description |
|---|---|---|
| base_on_id | string | Id of one of your saved introductions. Reuses its context and voice. Get ids from GET /api/v1/intro-options. |
| template | string | One of linkedin-headline, twitter-bio, instagram-bio, conference-speaker, community-intro, podcast-guest, portfolio-about. Sets format, length and audience, and outranks base_on_id. |
| use_profile | boolean | Fill gaps with the info saved in your account. Defaults to true. With a filled profile, name becomes optional. |
| use_style_reference | boolean | Match the voice of your previous introductions. Defaults to true. |
Priority, lowest to highest: saved profile, previous introduction, template, what you send in this request. A bad id or template returns 400 or 404 and never spends a credit.
| Field | Type | Required | Description |
|---|---|---|---|
| inputData.type | string | Yes | "bio" or "community" |
| inputData.language | string | Yes | Language for the introduction (e.g. "English", "Spanish") |
| inputData.name | string | Yes* | Person's name. *Optional when your saved profile or the introduction in base_on_id already has it |
| inputData.location | string | No | Location (city, country) |
| inputData.background.fields | string | No | Brief professional background |
| inputData.current_field | string | No | Current professional field |
| inputData.projects | string | No | Notable projects |
| inputData.passion | string | No | Passions and goals |
| inputData.achievements | string | No | Notable achievements |
| inputData.fun_fact | string | No | A fun fact about the person |
| inputData.types.Type | string | No | Introduction type (e.g. "Bio", "Event - In person") |
| inputData.types.Name | string | No | Where it'll be used, e.g. "LinkedIn", "Instagram", "Twitter", "Substack", "TikTok", "Conference" |
| inputData.types["Purpose of the introduction"] | string | No | Goal of the intro (e.g. "Make recruiters stop and connect") |
| inputData.types.Audience | string | No | Who will read it (e.g. "Recruiters and peers on LinkedIn") |
| inputData.types["Maximum length?"] | string | No | Max length, e.g. "Custom (300 characters)", "Twitter bio (150 characters)", "Instagram bio (150 characters)", "TikTok bio (80 characters)", "Not relevant" |
| inputData.types["Describe where are you going to use it"] | string | No | Extra context about where this intro will live |
| inputData.types.CTA | string | No | Call to action to include verbatim at the end |
| inputData.tone | string | No | Voice, e.g. "Professional", "Casual", "Friendly", "natural" |
| inputData.must_include | string | No | Specific points the intro MUST answer or mention (e.g. "Who you are, business name, who you help, how long in business"). Treated as mandatory. |
{
"introduction": "Hi, I'm Jane, a software engineer turned AI researcher based in San Francisco. I build open source NLP tools and speak at conferences like PyCon about making AI accessible to everyone. When I'm not coding, I'm probably doing something questionable at high altitude. Let's connect!",
"intro": "Same as introduction (kept for backward compatibility)",
"id": "a1b2c3d4e5f6g7h",
"based_on": {
"profile": true,
"template": "linkedin-headline",
"previous_introduction": null,
"style_reference_count": 3
}
}Rewrite an introduction you already have instead of starting from scratch. This is the same refine flow the app uses, so it keeps the person's voice, preserves their facts and respects the target platform's format and character limit. Costs one credit, refunded if the rewrite fails.
Pass an id to refine a saved introduction. The original context is rebuilt for you and the person's other introductions are fed in as a style reference, so you don't have to resend anything. To refine loose text instead, send introduction plus inputData.
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | No* | ID of a saved introduction to refine |
| introduction | string | No* | Raw text to refine, when you have no saved ID |
| adjustment | string | No** | One of shorter, longer, formal, casual, professional, creative |
| instruction | string | No** | Free-text instruction. Wins over the preset when they conflict |
| save | boolean | No | Overwrite the saved copy with the result. Needs id. Defaults to false |
| inputData | object | No | Generation context. Required only when refining raw text with no id |
* Send either id or introduction.
** Send at least one of adjustment or instruction.
curl -X POST https://api.intromakerai.com/api/v1/regenerate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"id": "abc123",
"adjustment": "shorter",
"instruction": "drop the emoji and keep the city",
"save": true
}'{
"introduction": "The rewritten text",
"revised": "Same as introduction (kept for backward compatibility)",
"id": "abc123",
"saved": true
}Conversational generation. Instead of filling in every field up front, you send the conversation so far and the assistant asks for whatever it still needs. When it has enough it writes the introduction and saves it.
Conversation turns are free. A credit is only spent on the turn that actually produces an introduction, the one that comes back with done: true. A conversation is capped at 25 user turns.
{
"messages": [
{ "role": "user", "content": "I need a bio for my LinkedIn profile" },
{ "role": "assistant", "content": "Happy to help. What's your name?" },
{ "role": "user", "content": "Jane Smith, I'm an AI engineer in Barcelona" }
]
}Send back the messages array from the previous response to continue the conversation.
{
"done": false,
"reply": "What would you like the bio to highlight?",
"messages": [ ... ]
}{
"done": true,
"reply": "Here's your introduction 👇",
"introduction": "The generated text",
"inputData": { ... },
"messages": [ ... ]
}Retrieve all your generated introductions (both web and API).
| Parameter | Type | Required | Description |
|---|---|---|---|
| page | number | No | Page number (default: 1) |
| limit | number | No | Items per page (default: 20, max: 100) |
| source | string | No | Filter by source: "api" or "web" |
curl https://api.intromakerai.com/api/v1/introductions?page=1&limit=10 \
-H "Authorization: Bearer YOUR_API_KEY"{
"introductions": [
{
"id": "uuid",
"title": "Jane Smith Introduction",
"content": "Hi, I'm Jane, a software engineer...",
"language": "English",
"created_at": "2026-03-16T10:30:00Z",
"source": "api"
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 42,
"total_pages": 5
}
}Fetch a single introduction by its ID. Free. You can only read introductions that belong to your account, anything else returns 404.
curl https://api.intromakerai.com/api/v1/introductions/abc123 \
-H "Authorization: Bearer YOUR_API_KEY"{
"id": "abc123",
"title": "Jane Smith Introduction",
"content": "The introduction text",
"language": "English",
"created_at": "2026-07-30T09:21:00.000Z",
"source": "api"
}Save an introduction you wrote yourself into your library. Free, no AI runs and no credit is spent. Use this when you already have the text and just want it stored alongside the generated ones.
| Field | Type | Required | Description |
|---|---|---|---|
| content | string | Yes | The introduction text |
| title | string | No | Library title. Defaults to "Saved introduction" |
| language | string | No | Defaults to "English" |
| tone | string | No | Defaults to "natural" |
curl -X POST https://api.intromakerai.com/api/v1/introductions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"title": "Conference bio",
"content": "I'"'"'m Jane, an AI engineer based in Barcelona."
}'Returns 201 with the saved introduction.
Update the text (or title) of one of your introductions, handy when you've refined the wording and want to save the final version. You can only update introductions that belong to your account. This does not use AI and does not count against your usage.
| Field | Type | Required | Description |
|---|---|---|---|
| content | string | No* | New introduction text |
| title | string | No* | New title |
*At least one of content or title is required.
curl -X PATCH https://api.intromakerai.com/api/v1/introductions/abc123 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{ "content": "Hey there! I'\''m Celia, based in Taipei..." }'{
"id": "abc123",
"title": "Celia Introduction",
"content": "Hey there! I'm Celia, based in Taipei...",
"language": "English",
"source": "web"
}Permanently delete one of your introductions by its ID. You can only delete introductions that belong to your account. Get the id from GET /api/v1/introductions.
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | The ID of the introduction to delete |
curl -X DELETE https://api.intromakerai.com/api/v1/introductions/abc123 \
-H "Authorization: Bearer YOUR_API_KEY"{
"success": true,
"id": "abc123"
}Everything you need to offer someone the same starting points the web app does, before spending a credit. Returns the personal info saved in the account, the introductions worth building on, the platform templates, and the current usage. Free, it never spends a credit. This is what the MCP tool get_introduction_options calls.
curl https://api.intromakerai.com/api/v1/intro-options \
-H "Authorization: Bearer YOUR_API_KEY"{
"profile": {
"has_info": true,
"name": "Jane Smith",
"location": "San Francisco",
"current_field": "AI & Machine Learning",
"tone": "Professional",
"language": "English"
},
"previous_introductions": [
{
"id": "a1b2c3d4e5f6g7h",
"title": "Jane Smith - LinkedIn headline",
"language": "English",
"created_at": "2026-07-30 09:41:34.735Z",
"source": "api",
"preview": "Software engineer turned AI researcher | Open source NLP…"
}
],
"templates": [
{ "id": "linkedin-headline", "name": "LinkedIn Headline", "description": "Short professional tagline for a LinkedIn profile" }
],
"usage": { "used": 4, "limit": 50, "remaining": 46, "plan": "paid" }
}Feed previous_introductions[].id back as base_on_id and templates[].id as template in POST /api/v1/generate.
Check how many introduction credits are left in the current period and when the allowance renews. This endpoint is free, it never spends a credit. Use it before generating in a script so you can stop cleanly instead of hitting a 403.
curl https://api.intromakerai.com/api/v1/usage \
-H "Authorization: Bearer YOUR_API_KEY"{
"used": 3,
"limit": 50,
"remaining": 47,
"renews_at": "2026-08-14T09:21:00.000Z",
"plan": "paid"
}| Field | Type | Description |
|---|---|---|
| used | number | Introductions generated in the current period |
| limit | number | Total allowance for the period |
| remaining | number | Credits still available, never negative |
| renews_at | string | ISO date when the allowance resets to zero |
| plan | string | "free" or "paid" |
The renewal cycle is monthly, anchored to your purchase date on a paid plan and to your signup date on the free plan.
Only calls that actually run the AI spend a credit. Reading, editing and organising your library is always free, and a credit is refunded automatically if the AI call fails, so a transient error never costs you anything.
| Endpoint | Cost |
|---|---|
| POST /api/v1/generate | 1 credit |
| POST /api/v1/regenerate | 1 credit |
| POST /api/v1/chat | 1 credit, only on the turn that returns an introduction |
| GET /api/v1/intro-options | Free |
| GET /api/v1/usage | Free |
| GET /api/v1/introductions | Free |
| GET /api/v1/introductions/:id | Free |
| POST /api/v1/introductions | Free |
| PATCH /api/v1/introductions/:id | Free |
| DELETE /api/v1/introductions/:id | Free |
API keys are limited to 100 requests per day. If you need a higher limit, contact us.
curl -X POST https://api.intromakerai.com/api/v1/generate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"inputData": {
"type": "Bio",
"language": "English",
"name": "Jane Smith",
"location": "San Francisco",
"background": { "fields": "Software engineer with 5 years of experience" },
"current_field": "AI & Machine Learning",
"projects": "Open source NLP tools",
"passion": "Making AI accessible to everyone",
"achievements": "Speaker at PyCon, 10k GitHub stars",
"fun_fact": "I once debugged code while skydiving",
"tone": "Professional",
"must_include": "Who I am, the name of my business, who I help, how long I have been in business",
"types": {
"Name": "LinkedIn",
"Purpose of the introduction": "Make recruiters and peers stop, read, and connect with me",
"Audience": "LinkedIn visitors (recruiters, peers, potential collaborators)",
"Maximum length?": "Custom (220 characters)",
"Describe where are you going to use it": "LinkedIn profile headline",
"CTA": "Let\'s connect!"
}
}
}'const response = await fetch(
"https://api.intromakerai.com/api/v1/generate",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_KEY",
},
body: JSON.stringify({
inputData: {
type: "Bio",
language: "English",
name: "Jane Smith",
location: "San Francisco",
background: { fields: "Software engineer with 5 years of experience" },
current_field: "AI & Machine Learning",
projects: "Open source NLP tools",
passion: "Making AI accessible to everyone",
achievements: "Speaker at PyCon, 10k GitHub stars",
fun_fact: "I once debugged code while skydiving",
tone: "Professional",
must_include: "Who I am, the name of my business, who I help, how long I have been in business",
types: {
"Name": "LinkedIn",
"Purpose of the introduction": "Make recruiters and peers stop, read, and connect with me",
"Audience": "LinkedIn visitors (recruiters, peers, potential collaborators)",
"Maximum length?": "Custom (220 characters)",
"Describe where are you going to use it": "LinkedIn profile headline",
"CTA": "Let's connect!",
},
},
}),
}
);
const data = await response.json();
console.log(data.introduction); // Your generated introductionimport requests
response = requests.post(
"https://api.intromakerai.com/api/v1/generate",
headers={
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_KEY",
},
json={
"inputData": {
"type": "Bio",
"language": "English",
"name": "Jane Smith",
"location": "San Francisco",
"background": {"fields": "Software engineer with 5 years of experience"},
"current_field": "AI & Machine Learning",
"projects": "Open source NLP tools",
"passion": "Making AI accessible to everyone",
"achievements": "Speaker at PyCon, 10k GitHub stars",
"fun_fact": "I once debugged code while skydiving",
"tone": "Professional",
"must_include": "Who I am, the name of my business, who I help, how long I have been in business",
"types": {
"Name": "LinkedIn",
"Purpose of the introduction": "Make recruiters and peers stop, read, and connect with me",
"Audience": "LinkedIn visitors (recruiters, peers, potential collaborators)",
"Maximum length?": "Custom (220 characters)",
"Describe where are you going to use it": "LinkedIn profile headline",
"CTA": "Let's connect!",
},
}
},
)
data = response.json()
print(data["introduction"]) # Your generated introduction| Status | Description |
|---|---|
| 401 | Missing or invalid API key |
| 429 | Rate limit exceeded (100 requests/day) |
| 400 | Missing required fields (inputData) |
| 404 | Introduction not found (or not owned by your account) |
| 500 | Server error during generation |