---
title: "SmartHR APIとは？従業員データの取得・更新とWebhook差分同期の実装手順【2026年10月】"
url: "https://www.issoh.co.jp/tech/details/18722/"
published: 2026-10-22
updated: 2026-10-22
categories: ["業務システム"]
publisher: "株式会社一創"
---

# SmartHR APIとは？従業員データの取得・更新とWebhook差分同期の実装手順【2026年10月】

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リファレンス](https://developer.smarthr.jp/api)で、その中身はSwagger 2.0形式の定義（[swagger\_doc](https://app.smarthr.jp/api/v1/swagger%5Fdoc)）として機械可読の形でも公開されています。

### 従業員・部署・給与明細など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がどこを担うかは[人事システムの機能と種類を整理した記事](https://www.issoh.co.jp/column/details/13557/)で確認できます。

### 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で従業員一覧を取得するまでの手順

ここからは手を動かす工程です。本番テナントに触れる前に、権限の絞り方と検証環境の用意を済ませておきます。

### 管理者だけが発行できるアクセストークンとリソース単位の参照・更新許可

トークンは、ホームのアプリ一覧から［共通設定］→［外部システム連携］→［アクセストークン］→［新規発行］で作ります（[公式ヘルプ「アクセストークンの発行方法」](https://support.smarthr.jp/ja/help/articles/360026266033/)）。発行できるのは管理者だけです。

発行画面では［リソースのアクセス許可］でリソースごとに参照と更新を選び、［従業員項目のアクセス許可］で読める項目を絞れます。ヘルプの対象プラン欄に載っているのは、2026年10月時点で労務管理・人事・労務エッセンシャル・¥0・HRストラテジーの4プランです。連携先ごとにトークンを分けておくと、1つの連携を止めたいときに他を巻き込まずに失効できます。

### 本番テナントを汚さずSandbox環境のdaruma.spaceで疎通する準備

公式の[Sandbox概要](https://developer.smarthr.jp/api/about%5Fsandbox)によると、検証用環境はフォームから申し込み、エンドポイントは`https://<サブドメイン>.daruma.space/api`になります。クレジットカードや事業所情報の入力は不要で、ダミーの従業員データが入った状態から始められます。

注意点は1つ。Sandboxのデータは予告なく削除されることがあると明記されています。テストデータの投入スクリプトを手元に残し、いつでも作り直せるようにしておくと検証が止まりません。

### curlでGET /v1/crewsを呼びper\_page=100で全件を数える手順

認証はBASIC認証とBearerヘッダーのどちらでも通ると[API概要](https://developer.smarthr.jp/api/about%5Fapi)に書かれています。ここでは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設計の記事](https://www.issoh.co.jp/tech/details/16179/)でも整理しています。

## Webhookで従業員の入退社や部署異動を受け取る受信側の設計

定期同期だけでは、入社初日のアカウント発行のように「当日中に反映したい」要件に間に合わないことがあります。そこでWebhookを組み合わせます。

### crew\_createdなど9種類のイベントと送られるペイロードの範囲

[Webhook概要](https://developer.smarthr.jp/api/about%5Fwebhook)に載っているイベントは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の作り方と受信側の実装を解説した記事](https://www.issoh.co.jp/tech/details/1378/)にまとめています。

### 約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](https://www.issoh.co.jp/tech/details/15962/)で組むほうが早く、保守も軽く済みます。

自作に踏み切るのは、連携先が自社開発の基幹システムや独自DBで既製コネクタが無い場合と、社員番号の付け替えや部署コードの変換など、変換規則が画面設定で表現しきれない場合です。この2つのどちらかに当たるなら、差分同期とWebhook受信を自社側で持つ構成が保守しやすくなります。設計から実装まで外部に任せたい場合は、一創の[API開発・システム連携](https://www.issoh.co.jp/service/system/api/)でご相談を受けています。

### 口座や家族情報まで読めるトークンを渡してはいけない見送り場面

従業員データには口座情報・家族情報・マイナンバーに近い個人情報が含まれます。Webhookの`crew`にも`bank_accounts`や緊急連絡先の項目があり、許可を広く取ったトークンは漏えい時の被害も広がります。

連携先が氏名・メール・部署しか使わないのに、作業を楽にするため全項目を許可したトークンを外部SaaSに預ける構成は採りません。IdPへのアカウント配布だけが目的なら、IdP側のプロビジョニング機能と標準規格の[SCIMによるID連携](https://www.issoh.co.jp/tech/details/15876/)で済むかを先に確かめます。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のコード例で解説](https://www.issoh.co.jp/tech/details/15351/)：SaaSに依存しないREST連携の基本手順とエラー処理
- [API連携ツールの選び方｜iPaaS・データ連携基盤・受託開発の使い分けを解説](https://www.issoh.co.jp/column/details/16376/)：自作とiPaaSの境界をコストと保守で比べる判断材料
- [Microsoft Graph APIとは？できること・権限設計とPythonでの呼び出し手順](https://www.issoh.co.jp/tech/details/18719/)：SmartHRの従業員データをMicrosoft 365のアカウントへ繋ぐ側のAPI
- [Slack APIとは？Web API・Events API・Boltの違いと実装手順](https://www.issoh.co.jp/tech/details/18717/)：入社や異動の通知をSlackへ流すときの送信側の実装
- [HubSpot APIの使い方｜認証の選び方とupsert・Webhook・レート制限の実装](https://www.issoh.co.jp/tech/details/18636/)：同じG39の業務SaaS APIで、upsertとWebhookの組み方を比較できる

---

出典: [SmartHR APIとは？従業員データの取得・更新とWebhook差分同期の実装手順【2026年10月】](<https://www.issoh.co.jp/tech/details/18722/>)（株式会社一創）
