HELP / API

公開APIの使い方

自分の経歴書を、外部のツールやAIエージェントから読み書きするためのAPIです。 取得・下書きの保存・公開の切り替えができ、ブラウザのフォームを開かずに作成から公開まで完了できます。

このページの実例はすべて、実際のスキーマと応答から生成しています。型の定義や、ここに載せていない細部は /api/v1/openapi.json (OpenAPI 3.1)が正です。実装するときは合わせて参照してください。

APIキーを発行する SETUP

  1. keirecにログインして 設定画面 を開きます。
  2. 「APIキー」の欄で用途のメモ(任意)を入れて「キーを発行」を押します。
  3. 表示された keirec_ で始まる文字列をコピーします。 この画面を閉じると二度と表示できません。 サーバーには復元できない形でしか保存していないためです。
  4. 手元では環境変数に置いて使ってください(例: KEIREC_API_KEY)。以降の実例はこの名前で書いています。

キーを持っている人は、あなたと同じ操作(下書きの保存・公開の切り替え)ができます。 リポジトリや設定ファイルにそのまま書かず、他の人にも渡さないでください。 漏れたかもしれないと思ったら、設定画面から個別に、または「すべて失効」で1操作で止められます。失効は即時に反映されます。

同時に持てるのは5本までです。用途別に分けて使い、使わなくなったものは失効してください。

認証 AUTHENTICATION

すべてのリクエストに Authorization: Bearer <APIキー> を付けます。 ログイン用のCookieでは通りません。次の節のcurlはどれもこのヘッダを付けた形で、キーを環境変数に入れていればそのまま実行できます。

NOTE ブラウザのJavaScriptから直接呼ぶことは想定していません(クロスオリジンの許可を出していません)。 APIキーはサーバー・CLI・エージェントの実行環境に置いてください。

エンドポイントとcurlの実例 ENDPOINTS

操作の対象は常に「APIキーの持ち主の経歴書1件」です。経歴書IDもユーザーIDも受け取らないので、他の人のデータを指定することはできません。

  • GET/api/v1/resume下書きと公開状態を取得する

    未保存なら空の内容と updatedAt: null が返ります。呼んでも経歴書は作られません。

    呼び出し方

    curl \
      -H "Authorization: Bearer $KEIREC_API_KEY" \
      https://keirec.com/api/v1/resume
  • PUT/api/v1/resume/draft下書きを保存する

    body は ResumeContent 全体です。部分更新はありません。公開中の内容には影響しません。

    呼び出し方

    curl -X PUT \
      -H "Authorization: Bearer $KEIREC_API_KEY" \
      -H "Content-Type: application/json" \
      -d @resume.json \
      https://keirec.com/api/v1/resume/draft
  • POST/api/v1/resume/publish下書きを公開する

    初回の公開で公開URLが決まります。以後は非公開にしても同じURLに戻せます。

    呼び出し方

    curl -X POST \
      -H "Authorization: Bearer $KEIREC_API_KEY" \
      https://keirec.com/api/v1/resume/publish
  • POST/api/v1/resume/unpublish公開を取り下げる

    公開URLは即座に404になります。

    呼び出し方

    curl -X POST \
      -H "Authorization: Bearer $KEIREC_API_KEY" \
      https://keirec.com/api/v1/resume/unpublish

NOTE 4本とも同じ形の応答を返します(content / isPublished / publicId / publicUrl / publishedAt / updatedAt)。 公開URLは publicUrl にそのまま入るので、組み立てる必要はありません(非公開のときは null です)。 パスの v1 はAPIのバージョンで、 content.version とは別物です。

公開・非公開の切り替えは、経歴書を人目に触れる状態にするかどうかの操作です。 エージェントに任せる場合も、利用者がはっきり指示したときだけ呼ぶようにしてください。

リクエストボディの実例 REQUEST BODY

ボディを送るのは PUT /api/v1/resume/draft だけです。 送るのは ResumeContent 全体で、部分更新はありません。 送った内容が下書きをまるごと置き換えますので、 既存の内容を残したいときは先にGETで取得し、その content を書き換えて送り返してください。

最小構成29行

これが受け付けられる最小の形です。`version` と `basic` と `sections` は省略できません(空でも入れます)。標準セクション(職務経歴・スキル・資格)は中身が空でよく、空のセクションは公開ページに出ません。

{
  "version": 1,
  "basic": {
    "name": "山田 太郎",
    "title": "ソフトウェアエンジニア / バックエンド・SRE",
    "summary": "",
    "links": []
  },
  "sections": [
    {
      "id": "workHistory",
      "visible": true,
      "type": "workHistory",
      "entries": []
    },
    {
      "id": "skills",
      "visible": true,
      "type": "skills",
      "groups": []
    },
    {
      "id": "qualifications",
      "visible": true,
      "type": "qualifications",
      "entries": []
    }
  ]
}

現実的なフル構成153行

/sample で公開している見本と同じ内容です。職務経歴・スキル・資格・自由記述(任意の英字ラベル付き)がひと通り入っています。`resume.json` に保存して、上のcurlでそのまま送れます。

JSONを開く(153行)
{
  "version": 1,
  "basic": {
    "name": "山田 太郎",
    "title": "ソフトウェアエンジニア / バックエンド・SRE",
    "summary": "Webアプリケーションのバックエンドを中心に7年ほど開発しています。\n**設計から運用まで一貫して担当する**のが得意で、直近はBtoB SaaSの基盤刷新と4名のチームリードを担当しました。\n\n- 得意領域: TypeScript / Go でのAPI開発、PostgreSQLのパフォーマンス改善\n- 関心: 計測にもとづく信頼性の改善と、開発者体験の底上げ",
    "links": [
      {
        "label": "GitHub",
        "url": "https://github.com/keirec-sample"
      },
      {
        "label": "ブログ",
        "url": "https://example.com/blog"
      }
    ]
  },
  "sections": [
    {
      "id": "sample-work",
      "visible": true,
      "type": "workHistory",
      "entries": [
        {
          "company": "株式会社クレアノーツ",
          "from": "2023-04",
          "to": "",
          "role": "リードエンジニア",
          "description": "BtoB SaaS(契約社数約400社)の開発チーム4名のリードとして、機能開発とアーキテクチャの意思決定を担当。\n\n- 自前実装の認証をOIDCベースに移行し、SSO対応を要件とする商談の失注をゼロに\n- 遅いエンドポイント上位20件をN+1解消とインデックス設計で改善し、p95応答を1.8秒から420msへ短縮\n- 手動だったリリースをGitHub Actionsに載せ替え、デプロイ頻度を週1回から日次へ\n- 設計レビューとオンボーディング資料を整備し、新メンバーの初コミットまでの日数を平均9日から3日へ",
          "tech": [
            "TypeScript",
            "Node.js",
            "PostgreSQL",
            "AWS",
            "Terraform",
            "Datadog"
          ]
        },
        {
          "company": "合同会社ノクトリンク",
          "from": "2020-07",
          "to": "2023-03",
          "role": "バックエンドエンジニア",
          "description": "飲食店向け予約管理サービスのAPI開発と運用を担当。月間予約数は約12万件。\n\n- 予約枠の在庫管理をトランザクション設計から見直し、繁忙期の二重予約を解消\n- モノリスから通知基盤だけを切り出し、ピーク時のレスポンス劣化を回避\n- オンコール当番として障害対応と再発防止のポストモーテム運用を整備",
          "tech": [
            "Go",
            "MySQL",
            "Redis",
            "Docker",
            "GCP"
          ]
        },
        {
          "company": "株式会社トリムノス",
          "from": "2019-04",
          "to": "2020-06",
          "role": "Webエンジニア",
          "description": "新卒入社。社内業務システムの受託開発に従事し、要件のヒアリングから実装・保守までを担当。\n\n- 勤怠管理システムの改修を1人称で担当し、月次の集計作業を約8時間から30分へ短縮\n- 手作業だったテストにE2Eテストを導入し、リグレッションの再発を防止",
          "tech": [
            "PHP",
            "Laravel",
            "MySQL",
            "jQuery"
          ]
        }
      ]
    },
    {
      "id": "sample-skills",
      "visible": true,
      "type": "skills",
      "groups": [
        {
          "category": "言語",
          "items": [
            "TypeScript",
            "Go",
            "Python",
            "SQL"
          ]
        },
        {
          "category": "フレームワーク",
          "items": [
            "Nuxt",
            "NestJS",
            "Echo",
            "Laravel"
          ]
        },
        {
          "category": "インフラ",
          "items": [
            "AWS (ECS / RDS / Lambda)",
            "Cloudflare Workers",
            "Terraform",
            "GitHub Actions"
          ]
        },
        {
          "category": "データストア",
          "items": [
            "PostgreSQL",
            "MySQL",
            "Redis"
          ]
        },
        {
          "category": "監視・運用",
          "items": [
            "Datadog",
            "Sentry",
            "OpenTelemetry"
          ]
        }
      ]
    },
    {
      "id": "sample-qualifications",
      "visible": true,
      "type": "qualifications",
      "entries": [
        {
          "name": "AWS Certified Solutions Architect – Associate",
          "year": "2023"
        },
        {
          "name": "応用情報技術者",
          "year": "2020"
        },
        {
          "name": "基本情報技術者",
          "year": "2018"
        }
      ]
    },
    {
      "id": "sample-about",
      "visible": true,
      "type": "freeText",
      "title": "自己PR",
      "body": "課題を「速く直す」よりも、**同じ障害が二度起きない形に直す**ことを重視しています。\n直近ではオンコールの負荷が特定メンバーに偏っていたため、アラートの棚卸しとRunbookの整備を提案し、月あたりの深夜対応を6件から1件に減らしました。\n\nチームでは、設計の意図をドキュメントとして残すこと、レビューで指摘だけでなく代替案を示すことを心がけています。\n今後はプロダクトの信頼性設計により深く関わり、SLOにもとづく意思決定をチームに定着させたいと考えています。"
    },
    {
      "id": "sample-talks",
      "visible": true,
      "type": "custom",
      "title": "登壇・執筆",
      "body": "- 社内勉強会で「N+1をやめるためのORMの読み方」を月1で開催(累計12回)\n- 技術ブログでSLO運用の立ち上げについて執筆(累計8本)",
      "enLabel": "TALKS"
    }
  ]
}

NOTEsummary ・ description ・自由記述の body にはMarkdownが書けます。使える記法は Markdown記法の解説 を参照してください。セクションの並び順は配列の順そのままで、順序を指定するフィールドはありません。

主要フィールドと上限 FIELDS

文字数と件数の上限です。超えると422で返り、errors[] がどのフィールドかを教えてくれるので、 先に全部を覚える必要はありません。型と既定値まで含めた定義は /api/v1/openapi.json にあります。

基本情報(basic)

フィールド 内容 上限
basic.name氏名100文字
basic.title肩書き100文字
basic.summary自己紹介(Markdown可)2000文字
basic.linksリンク10件
basic.links[].labelリンクの表示名50文字

セクション全体(sections)

フィールド 内容 上限
sectionsセクション30件

職務経歴(type: workHistory)

フィールド 内容 上限
entries職歴30件
entries[].company会社名100文字
entries[].role役割100文字
entries[].description業務内容(Markdown可)5000文字
entries[].tech技術タグ30件
entries[].tech[]技術タグ1つ30文字

スキル(type: skills)

フィールド 内容 上限
groupsカテゴリ20件
groups[].categoryカテゴリ名50文字
groups[].items1カテゴリの項目50件
groups[].items[]項目1つ50文字

資格(type: qualifications)

フィールド 内容 上限
entries資格30件
entries[].name資格名100文字
entries[].year取得年20文字

自由記述(type: freeText / custom)

フィールド 内容 上限
title見出し50文字
body本文(Markdown可)10000文字
enLabel英字ラベル(任意・半角英数と記号のみ)30文字

応答の実例 RESPONSES

4本とも同じ形を返します。ここではGETの応答を、まだ何も保存していないときと公開中のときで並べます。

まだ何も保存していないとき

空の内容と updatedAt: null が返ります。経歴書の行はこの時点では作られていません(作られるのは最初のPUTです)。

リクエスト

curl \
  -H "Authorization: Bearer $KEIREC_API_KEY" \
  https://keirec.com/api/v1/resume

応答

{
  "content": {
    "version": 1,
    "basic": {
      "name": "",
      "title": "",
      "summary": "",
      "links": []
    },
    "sections": [
      {
        "id": "workHistory",
        "type": "workHistory",
        "visible": true,
        "entries": []
      },
      {
        "id": "skills",
        "type": "skills",
        "visible": true,
        "groups": []
      },
      {
        "id": "qualifications",
        "type": "qualifications",
        "visible": true,
        "entries": []
      }
    ]
  },
  "isPublished": false,
  "publicId": null,
  "publishedAt": null,
  "updatedAt": null,
  "publicUrl": null,
  "hasViewPassword": false
}

公開中のとき

content には保存済みの下書きがそのまま入ります(ここでは上の「最小構成」を保存した場合)。isPublished が true になり、publicUrl に公開ページの絶対URLがそのまま入るので、組み立てる必要はありません。非公開に戻すと publicUrl は null になりますが、publicId は残るので同じURLに再公開できます。hasViewPassword が true のときは公開URLに閲覧パスワードが掛かっていて、開くには入力が必要です。この値は読み取り専用で、設定・変更・解除はAPIからは行えません(編集画面の「公開」パネルから行います)。

リクエスト

curl \
  -H "Authorization: Bearer $KEIREC_API_KEY" \
  https://keirec.com/api/v1/resume

応答

{
  "content": {
    "version": 1,
    "basic": {
      "name": "山田 太郎",
      "title": "ソフトウェアエンジニア / バックエンド・SRE",
      "summary": "",
      "links": []
    },
    "sections": [
      {
        "id": "workHistory",
        "visible": true,
        "type": "workHistory",
        "entries": []
      },
      {
        "id": "skills",
        "visible": true,
        "type": "skills",
        "groups": []
      },
      {
        "id": "qualifications",
        "visible": true,
        "type": "qualifications",
        "entries": []
      }
    ]
  },
  "isPublished": true,
  "publicId": "example-public-id-012",
  "publishedAt": "2026-08-01T03:00:00.000Z",
  "updatedAt": "2026-08-01T02:58:41.204Z",
  "publicUrl": "https://keirec.com/r/example-public-id-012",
  "hasViewPassword": true
}

エラーと対処 ERRORS

エラーはすべて { code, message } の形で返ります。 code は機械可読なので、そのまま分岐に使えます。

状態 code 対処
400bad_requestリクエストボディがJSONとして読めません。送信内容を確認してください。
401unauthorizedキーが無い・形式が違う・失効しています。理由は区別しません。設定画面で発行し直してください。
404not_found公開できる経歴書がありません。先に下書きを保存してください。
409key_limit_reachedキーが上限の5本に達しています。使っていないキーを失効してください。
422validation_failederrors[] に「どのフィールドがなぜ不正か」が入ります。そこを直して送り直してください。
429rate_limitedRetry-After 秒だけ待ってから再試行してください(60秒)。
500internal_errorサーバー側の問題です。時間をおいて再試行してください。

401 — キーが正しくない

ヘッダが無いときも、形式が違うときも、失効済みのキーのときも同じ本文が返ります。応答の違いから「そのキーが存在するか」を判定できないようにするためです。

{
  "code": "unauthorized",
  "message": "APIキーが正しくありません"
}

422 — フィールド単位で不正の場所が返る

氏名を上限より1文字長くし、リンクのURLからスキーム(https://)を落とし、期間を 2021/04 と書いて送ったときの応答です。path が直す場所を指すので、上限値を知らなくても返ってきた内容だけで直せます。

{
  "code": "validation_failed",
  "message": "入力内容に誤りがあります",
  "errors": [
    {
      "path": "basic.name",
      "message": "Too big: expected string to have <=100 characters"
    },
    {
      "path": "basic.links.0.url",
      "message": "Invalid URL"
    },
    {
      "path": "sections.0.entries.0.from",
      "message": "Invalid string: must match pattern /^\\d{4}-(0[1-9]|1[0-2])$/"
    }
  ]
}

429 — fair-use制限を超えた

Retry-After の秒数だけ空けて再試行してください。待てば回復するので、キーを発行し直す必要はありません。

retry-after: 60

{
  "code": "rate_limited",
  "message": "fair-use制限(60リクエスト/60秒)を超えました。60秒ほど待てば再び呼び出せます"
}

NOTE 422のときだけ errors[] が付き、 { path, message } の形で不正なフィールドを1つずつ示します。 path は basic.name のようなドット区切りで、配列は sections.0.entries.0.from のように添字が入ります。 上限値を知らなくても、返ってきた内容を読めば直せます。

レート制限 RATE LIMIT

APIキー1本あたり60秒あたり60リクエストまでです。 超えると429と Retry-After が返ります。待てば回復するので、その秒数だけ空けて再試行してください。

これは暴走したループを止めるためのfair-useの目安で、使用量を課金するためのものではありません。 経歴書1件を作って公開するのに必要な呼び出しは多くても十数回なので、通常の使い方で当たることはありません。 制限は認証が要る4本にだけ掛かります。

利用条件 TERMS

APIと外部連携の利用条件は 利用規約 の第6条(APIおよび外部連携)に定めています。キーの管理責任・fair-use制限・キーの共有と再配布の禁止のほか、 無料で提供しているため、予告なく内容を変更したり提供を停止したりすることがあることを含みます。 利用の前に一度お読みください。