SmartHR APIは、SmartHRに登録された従業員・部署・雇用形態・給与明細などのデータを、自社システムからHTTPで読み書きするためのREST APIです。入社や異動のたびに勤怠・基幹・社内ポータルへ同じ情報を手で転記している状態を、APIとWebhookの組み合わせで置き換えられます。この記事では、2026年10月時点の公式ドキュメントとSwagger定義をもとに、アクセストークンの許可設定、curlでの疎通、Pythonでの差分同期、Webhook受信の作り方までを順に示します。最後に、自作せずノーコード連携で済ませるべき場面も条件付きで整理しました。
まとめ:SmartHR APIで従業員データを自社システムへ繋ぐ前に押さえる結論
SmartHR APIはテナントごとのサブドメイン配下(https://<サブドメイン>.smarthr.jp/api)で提供され、管理者が発行したアクセストークンで認証します。トークンごとに毎秒10回・毎時5000回の上限があり、一覧取得は1ページ最大100件です。
差分同期は「一覧APIを更新日時の降順で読み、前回同期時刻より古い行が出たら止める」形と、「Webhookで変更通知を受ける」形の2つを併用するのが堅実です。一覧APIには更新日時の絞り込みパラメーターが無いため、全件取得を毎回回す設計は人数が増えるほど上限に当たります。
トークンの許可は連携先が使う項目だけに絞ります。口座や家族の情報まで読めるトークンを外部ツールへ渡す構成は採りません。勤怠や給与など主要SaaSとの既製連携で要件が満たせるなら、APIの自作は見送ってかまいません。
SmartHR APIで扱えるリソースと従業員データ連携の全体像
まず、APIで何に触れられるのかを把握します。仕様の正本はSmartHR APIリファレンスで、その中身はSwagger 2.0形式の定義(swagger_doc)として機械可読の形でも公開されています。
従業員・部署・給与明細など60パスに広がるAPIリファレンスの範囲
2026年10月22日に取得したSwagger定義を数えると、パスは60本、操作(メソッドとパスの組)は120本、分類タグは27種類でした。中心は従業員(/v1/crews)で、一覧・登録・取得・部分更新・削除・招待の操作がそろっています。
| 分類 | 主なパス | 操作 |
|---|---|---|
| 従業員・家族 | /v1/crews ほか | 参照・登録・更新・削除 |
| 組織マスタ | 部署・役職・等級・職種 | 参照・登録・更新・削除 |
| 給与明細 | /v1/payrolls ほか | 登録・確定・公開 |
| 源泉徴収票 | /v1/tax_withholdings | 参照・登録・更新・削除 |
| アカウント | /v1/users ほか | 参照・SAML設定 |
| 一括処理 | /v1/batch_jobs | CSV出力・一括登録 |
給与明細は外部の給与計算システムで計算した結果を登録し、確定(fix)と公開(publish)までAPIで進められます。SmartHRを「従業員マスタの正本」とし、周辺システムを従属させる構成を組みやすい範囲です。人事システム全体の中でSmartHRがどこを担うかは人事システムの機能と種類を整理した記事で確認できます。
APIとWebhookとCSVの3経路を通信の向きと鮮度で比較する表
SmartHRから外へデータを出す経路は、APIで取りに行く、Webhookで押してもらう、CSVで書き出す、の3つです。向きと鮮度が違うため、用途で分けます。
| 経路 | 向き | 鮮度 | 向く用途 |
|---|---|---|---|
| REST API | 自社から取得・更新 | 呼んだ時点 | 定期同期・書き戻し |
| Webhook | SmartHRから通知 | 変更の直後 | 入社・異動の即時反映 |
| CSV出力 | 画面またはAPIで生成 | 出力した時点 | 月次の一括処理 |
CSV出力もAPIから起動できます。POST /v1/batch_jobs/job_types/crew_exportに、事前に画面で作ったダウンロード形式のIDを渡すと、バックグラウンド処理としてファイルが作られます。数千人規模の全件を月1回まとめて読むなら、ページングで100件ずつ取るよりこちらが上限に優しい方法です。
アクセストークン発行からcurlで従業員一覧を取得するまでの手順
ここからは手を動かす工程です。本番テナントに触れる前に、権限の絞り方と検証環境の用意を済ませておきます。
管理者だけが発行できるアクセストークンとリソース単位の参照・更新許可
トークンは、ホームのアプリ一覧から[共通設定]→[外部システム連携]→[アクセストークン]→[新規発行]で作ります(公式ヘルプ「アクセストークンの発行方法」)。発行できるのは管理者だけです。
発行画面では[リソースのアクセス許可]でリソースごとに参照と更新を選び、[従業員項目のアクセス許可]で読める項目を絞れます。ヘルプの対象プラン欄に載っているのは、2026年10月時点で労務管理・人事・労務エッセンシャル・¥0・HRストラテジーの4プランです。連携先ごとにトークンを分けておくと、1つの連携を止めたいときに他を巻き込まずに失効できます。
本番テナントを汚さずSandbox環境のdaruma.spaceで疎通する準備
公式のSandbox概要によると、検証用環境はフォームから申し込み、エンドポイントはhttps://<サブドメイン>.daruma.space/apiになります。クレジットカードや事業所情報の入力は不要で、ダミーの従業員データが入った状態から始められます。
注意点は1つ。Sandboxのデータは予告なく削除されることがあると明記されています。テストデータの投入スクリプトを手元に残し、いつでも作り直せるようにしておくと検証が止まりません。
curlでGET /v1/crewsを呼びper_page=100で全件を数える手順
認証はBASIC認証とBearerヘッダーのどちらでも通るとAPI概要に書かれています。ここではBearerヘッダーを使い、取得項目をfieldsで絞って呼びます。
export SMARTHR_SUBDOMAIN=your-tenant
export SMARTHR_TOKEN=xxxxxxxxxxxxxxxx
# -D - でレスポンスヘッダーも表示し、本文は crews.json に保存する
curl -s -D - -o crews.json \
-H "Authorization: Bearer ${SMARTHR_TOKEN}" \
"https://${SMARTHR_SUBDOMAIN}.smarthr.jp/api/v1/crews?per_page=100&page=1&sort=-updated_at&fields=id,emp_code,last_name,first_name,email,emp_status,updated_at"
ヘッダーのx-total-countが全件数、Linkが次ページのURL(rel="next")を示します。per_pageの既定値は10、最大は100です。401が返るならトークンの誤り、403でtypeがip_not_permittedならアクセス元の制限に当たっています。
Pythonで従業員データを差分同期するページングと更新の実装
疎通できたら、定期実行する同期処理に組み上げます。ここでの課題は、毎回全件を読まずに変わった分だけを拾うことです。
updated_at絞り込みが無い一覧APIで差分を拾うsortの使い方
Swagger定義でGET /v1/crewsの引数を見ると、社員番号・在籍状況・入社日・退職日・部署IDなどの絞り込みはありますが、更新日時の範囲指定はありません。そこでsort=-updated_atで新しい順に並べ、前回同期時刻より古い行に当たった時点で読むのをやめます。
import os
import time
import requests
BASE = f"https://{os.environ['SMARTHR_SUBDOMAIN']}.smarthr.jp/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['SMARTHR_TOKEN']}"}
FIELDS = "id,emp_code,last_name,first_name,email,emp_status,resigned_at,updated_at"
LIMITS = [ # (残り回数のヘッダー, リセットまでの秒数のヘッダー)
("x-rate-limit-remaining-second", "x-rate-limit-reset-second"),
("x-rate-limit-remaining", "x-rate-limit-reset"),
("x-intensive-rate-limit-remaining", "x-intensive-rate-limit-reset"),
]
def wait_seconds(headers):
# 残り0になっている上限のリセット秒数だけ待つ。どれか不明なら1秒
for remain, reset in LIMITS:
if headers.get(remain) == "0":
return int(headers.get(reset, "1")) + 1
return 1
def get(path, params):
while True:
r = requests.get(BASE + path, headers=HEADERS, params=params, timeout=30)
if r.status_code == 429:
time.sleep(wait_seconds(r.headers))
continue
r.raise_for_status()
if r.headers.get("x-rate-limit-remaining-second") == "0":
time.sleep(1)
return r
def changed_since(last_synced_at):
page = 1
while True:
r = get("/crews", {"sort": "-updated_at", "per_page": 100,
"page": page, "fields": FIELDS})
for crew in r.json():
if crew["updated_at"] <= last_synced_at:
return
yield crew
if 'rel="next"' not in r.headers.get("Link", ""):
return
page += 1
if __name__ == "__main__":
for crew in changed_since("2026-10-21T00:00:00.000Z"):
print(crew["id"], crew["emp_code"], crew["emp_status"], crew["updated_at"])
updated_atはUTCのISO 8601文字列(例:2021-03-17T00:00:00.000Z)で返るため、同じ形式どうしなら文字列比較で前後を判定できます。読んでいる最中に別の従業員が更新されると、並びがずれて1件を読み落とす可能性があるため、注意が必要です。前回同期時刻を数分戻して読み、受け側を従業員IDでの上書き(upsert)にしておけば、重複は害にならず取りこぼしだけを防げます。
PUT非推奨後のPATCH部分更新と必須7項目を欠けさせない更新ルール
自社システム側の変更をSmartHRへ書き戻すときはPATCH /v1/crews/{id}を使います。リファレンスによると、2020年12月16日以降の従業員更新は部分更新のみの提供で、PUTは呼べても挙動はPATCHと同じです。
見落としやすいのが必須項目の扱いです。姓・名・姓のよみ・名のよみ・性別・生年月日・住所の7項目が入力済みの従業員に対し、いずれかを空にする更新は受け付けられません。連携元で住所が未入力のまま空文字を送る実装は400で落ちます。送るのは変わった項目だけ、と決めておけば避けられます。
毎秒10回・毎時5000回の上限と429を避ける待機の組み込み
リクエスト上限は3層です。トークンごとに毎秒10回と毎時5000回、テナントのサブドメイン全体で毎分50000回。超えるとtoo_many_requests(429)が返り、各層の残り回数とリセットまでの秒数がヘッダーで分かれて届きます。
先のコードのwait_secondsは、残り0になった層のリセット秒数だけ待つ作りです。毎時上限のリセット値を常に採ると、毎秒上限に当たっただけで最大1時間止まってしまうため、層を見分けています。1万人を100件ずつ読めば100回で済みますが、従業員ごとに詳細取得を1回ずつ呼ぶ設計では1万回となり、毎時5000回の枠を2倍上回る計算です。上限設計の考え方はAPIレート制限の5方式と429設計の記事でも整理しています。
Webhookで従業員の入退社や部署異動を受け取る受信側の設計
定期同期だけでは、入社初日のアカウント発行のように「当日中に反映したい」要件に間に合わないことがあります。そこでWebhookを組み合わせます。
crew_createdなど9種類のイベントと送られるペイロードの範囲
Webhook概要に載っているイベントは9種類です。従業員の作成・更新・完全削除・ファイル取込(crew_created/crew_updated/crew_deleted/crew_imported)、家族情報の同じ4種、申請の最終承認(workflow_approved)です。
送信データはevent・triggered_at・sender(操作した従業員)と、イベントごとのオブジェクトで構成されます。従業員系では一覧APIと同じ構造のcrewが入りますが、出力項目は限定されています。必要な項目が欠けていたら、受け取ったidで詳細を取り直す前提で設計しておくと安全です。Webhookの登録自体もPOST /v1/webhooksで自動化できます。
X-SmartHR-Tokenの照合と60秒以内に200番台を返す受信処理
Webhookの設定時に任意のシークレットトークンを登録すると、通知にX-SmartHR-Tokenヘッダーとして付いてきます。受信側はこの値を定数時間比較で照合し、処理は後回しにして先に応答を返します。
import hmac
import json
import os
from flask import Flask, request, abort
app = Flask(__name__)
SECRET = os.environ["SMARTHR_WEBHOOK_TOKEN"]
seen = set() # 本番では Redis や DB の一意制約に置き換える
@app.post("/hooks/smarthr")
def smarthr_hook():
if not hmac.compare_digest(request.headers.get("X-SmartHR-Token", ""), SECRET):
abort(401)
body = request.get_json(silent=True) or {}
crew = body.get("crew") or {}
key = f'{body.get("event")}:{crew.get("id")}:{crew.get("updated_at")}'
if key not in seen:
seen.add(key)
with open("smarthr_events.jsonl", "a", encoding="utf-8") as f:
f.write(json.dumps(body, ensure_ascii=False) + "\n")
return "", 204
200番台以外を返すか、60秒以内に応答しないと送信失敗とみなされます。受信URLはSmartHRのサーバーから届く場所に置く必要があり、プライベートネットワークのアドレスは使えません。署名検証と冪等性の一般的な作り方はWebhookの作り方と受信側の実装を解説した記事にまとめています。
約3日で最大17回の再送と上限到達で無効化される運用上の落とし穴
失敗した通知は約3日間で最大17回まで再送されます(Sandboxでは約8分で最大5回)。上限に達するとそのWebhookは無効化されるため、受信サーバーの長期停止や証明書切れを放置すると、復旧後も通知が来ない状態が続きます。
対策は2つです。再送で同じ通知が重複して届く前提で、上のコードのようにイベント・従業員ID・更新時刻で重複を除くこと。もう1つは、Webhookが止まっても一覧APIの差分同期で追いつける作りにしておくことです。GET /v1/webhooksで設定の状態を定期的に読み、無効化を監視に乗せておくと気づきが早まります。
SmartHR APIを自作連携に使う条件とノーコード連携で済ませる場面
最後に、APIを自分で叩くべきかどうかの線引きです。
既製の連携アプリやiPaaSで足りる要件と自作に踏み切る判断基準
勤怠・給与・会計など主要SaaSとの連携は、SmartHR側や連携先側に既製の連携機能がある組み合わせが多くあります。新入社員を別SaaSへ登録する、部署変更をチャットに通知する程度なら、Yoomのような国産iPaaSで組むほうが早く、保守も軽く済みます。
自作に踏み切るのは、連携先が自社開発の基幹システムや独自DBで既製コネクタが無い場合と、社員番号の付け替えや部署コードの変換など、変換規則が画面設定で表現しきれない場合です。この2つのどちらかに当たるなら、差分同期とWebhook受信を自社側で持つ構成が保守しやすくなります。設計から実装まで外部に任せたい場合は、一創のAPI開発・システム連携でご相談を受けています。
口座や家族情報まで読めるトークンを渡してはいけない見送り場面
従業員データには口座情報・家族情報・マイナンバーに近い個人情報が含まれます。Webhookのcrewにもbank_accountsや緊急連絡先の項目があり、許可を広く取ったトークンは漏えい時の被害も広がります。
連携先が氏名・メール・部署しか使わないのに、作業を楽にするため全項目を許可したトークンを外部SaaSに預ける構成は採りません。IdPへのアカウント配布だけが目的なら、IdP側のプロビジョニング機能と標準規格のSCIMによるID連携で済むかを先に確かめます。APIで読む項目はfieldsとトークンの項目許可の両方で絞り、二重に防ぎます。
SmartHR APIの料金・プラン・SSO連携に関するよくある質問
実装の相談でよく出る質問を、公式ドキュメントの記述に沿って回答します。
SmartHR APIは追加料金なしで使えますか?
アクセストークン発行のヘルプでは、対象プランとして労務管理・人事・労務エッセンシャル・¥0・HRストラテジーが挙がっています(2026年10月時点)。API利用そのものに別料金がかかるかどうかは、確認した公式ページには記載がありませんでした。契約中のプランで発行画面が開けるかを管理者に確かめ、不明点は契約窓口へ問い合わせるのが確実です。
BASIC認証とBearerヘッダーはどちらを使えばよいですか?
公式のAPI概要では、トークンをBASIC認証のユーザー名として渡す方法と、Authorization: Bearerヘッダーで渡す方法の両方が案内されています。機能上の差は書かれていないため、どちらで実装しても動く仕様です。新規に作るなら、Slack APIやMicrosoft Graph APIなど他のSaaS APIと同じ書き方にそろえられるBearerヘッダーを勧めます。HTTPクライアントの共通処理を使い回せるためです。
SAML SSOのアカウントもAPIで作成できますか?
作成できます。Swagger定義には/v1/users/{id}/saml_userへの取得・作成・更新・削除と、有効化・無効化が定義されており、APIから操作可能です。従業員の登録とあわせてSSOアカウントまで用意する処理を、入社手続きの一部として自動化できます。対象ユーザーのIDはGET /v1/usersで引きます。
外部の給与計算システムの結果をAPIで給与明細にできますか?
できます。給与明細グループをPOST /v1/payrollsで作り、明細を/v1/payrolls/{payroll_id}/payslips/bulkで一括登録したあと、確定(fix)と公開(publish)を呼ぶ流れです。確定後に計算誤りが見つかった場合に備えて、確定解除(unfix)の操作も用意されています。
Webhookの受信先に社内ネットワークのサーバーを指定できますか?
指定できません。Webhook概要に、SmartHRのサーバーからアクセスできないURLやプライベートネットワークのアドレスは使えないと書かれています。社内システムで受けたい場合は、インターネットから届く受信用の小さな窓口をクラウド側に置き、そこから社内へキュー経由で渡す構成が現実的です。
関連記事
- API連携の実装方法:REST・認証・エラー処理の手順をcurlとPythonのコード例で解説:SaaSに依存しないREST連携の基本手順とエラー処理
- API連携ツールの選び方|iPaaS・データ連携基盤・受託開発の使い分けを解説:自作とiPaaSの境界をコストと保守で比べる判断材料
- Microsoft Graph APIとは?できること・権限設計とPythonでの呼び出し手順:SmartHRの従業員データをMicrosoft 365のアカウントへ繋ぐ側のAPI
- Slack APIとは?Web API・Events API・Boltの違いと実装手順:入社や異動の通知をSlackへ流すときの送信側の実装
- HubSpot APIの使い方|認証の選び方とupsert・Webhook・レート制限の実装:同じG39の業務SaaS APIで、upsertとWebhookの組み方を比較できる