vx0p.
Rust

reqwest

An ergonomic, batteries-included HTTP Client for Rust.

GitHub REST API

このページでは全体を通してGitHub REST APIを例に使用します。試しにcurlでAPIを叩いてみます。

対象リポジトリはhttps://github.com/rust-lang/rust

curl -L \
  -H "Accept: application/vnd.github+json" \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  https://api.github.com/repos/rust-lang/rust
実行結果
{
  "id": 724712,
  "node_id": "MDEwOlJlcG9zaXRvcnk3MjQ3MTI=",
  "name": "rust",
  "full_name": "rust-lang/rust",
  "private": false,
  "owner": {
    "login": "rust-lang",
    "id": 5430905,
    "node_id": "MDEyOk9yZ2FuaXphdGlvbjU0MzA5MDU=",
    "avatar_url": "https://avatars.githubusercontent.com/u/5430905?v=4",
    "gravatar_id": "",
    "url": "https://api.github.com/users/rust-lang",
    "html_url": "https://github.com/rust-lang",
    "followers_url": "https://api.github.com/users/rust-lang/followers",
    "following_url": "https://api.github.com/users/rust-lang/following{/other_user}",
    "gists_url": "https://api.github.com/users/rust-lang/gists{/gist_id}",
    "starred_url": "https://api.github.com/users/rust-lang/starred{/owner}{/repo}",
    "subscriptions_url": "https://api.github.com/users/rust-lang/subscriptions",
    "organizations_url": "https://api.github.com/users/rust-lang/orgs",
    "repos_url": "https://api.github.com/users/rust-lang/repos",
    "events_url": "https://api.github.com/users/rust-lang/events{/privacy}",
    "received_events_url": "https://api.github.com/users/rust-lang/received_events",
    "type": "Organization",
    "user_view_type": "public",
    "site_admin": false
  },
  "html_url": "https://github.com/rust-lang/rust",
  "description": "Empowering everyone to build reliable and efficient software.",
  "fork": false,
  "url": "https://api.github.com/repos/rust-lang/rust",
  "forks_url": "https://api.github.com/repos/rust-lang/rust/forks",
  "keys_url": "https://api.github.com/repos/rust-lang/rust/keys{/key_id}",
  "collaborators_url": "https://api.github.com/repos/rust-lang/rust/collaborators{/collaborator}",
  "teams_url": "https://api.github.com/repos/rust-lang/rust/teams",
  "hooks_url": "https://api.github.com/repos/rust-lang/rust/hooks",
  "issue_events_url": "https://api.github.com/repos/rust-lang/rust/issues/events{/number}",
  "events_url": "https://api.github.com/repos/rust-lang/rust/events",
  "assignees_url": "https://api.github.com/repos/rust-lang/rust/assignees{/user}",
  "branches_url": "https://api.github.com/repos/rust-lang/rust/branches{/branch}",
  "tags_url": "https://api.github.com/repos/rust-lang/rust/tags",
  "blobs_url": "https://api.github.com/repos/rust-lang/rust/git/blobs{/sha}",
  "git_tags_url": "https://api.github.com/repos/rust-lang/rust/git/tags{/sha}",
  "git_refs_url": "https://api.github.com/repos/rust-lang/rust/git/refs{/sha}",
  "trees_url": "https://api.github.com/repos/rust-lang/rust/git/trees{/sha}",
  "statuses_url": "https://api.github.com/repos/rust-lang/rust/statuses/{sha}",
  "languages_url": "https://api.github.com/repos/rust-lang/rust/languages",
  "stargazers_url": "https://api.github.com/repos/rust-lang/rust/stargazers",
  "contributors_url": "https://api.github.com/repos/rust-lang/rust/contributors",
  "subscribers_url": "https://api.github.com/repos/rust-lang/rust/subscribers",
  "subscription_url": "https://api.github.com/repos/rust-lang/rust/subscription",
  "commits_url": "https://api.github.com/repos/rust-lang/rust/commits{/sha}",
  "git_commits_url": "https://api.github.com/repos/rust-lang/rust/git/commits{/sha}",
  "comments_url": "https://api.github.com/repos/rust-lang/rust/comments{/number}",
  "issue_comment_url": "https://api.github.com/repos/rust-lang/rust/issues/comments{/number}",
  "contents_url": "https://api.github.com/repos/rust-lang/rust/contents/{+path}",
  "compare_url": "https://api.github.com/repos/rust-lang/rust/compare/{base}...{head}",
  "merges_url": "https://api.github.com/repos/rust-lang/rust/merges",
  "archive_url": "https://api.github.com/repos/rust-lang/rust/{archive_format}{/ref}",
  "downloads_url": "https://api.github.com/repos/rust-lang/rust/downloads",
  "issues_url": "https://api.github.com/repos/rust-lang/rust/issues{/number}",
  "pulls_url": "https://api.github.com/repos/rust-lang/rust/pulls{/number}",
  "milestones_url": "https://api.github.com/repos/rust-lang/rust/milestones{/number}",
  "notifications_url": "https://api.github.com/repos/rust-lang/rust/notifications{?since,all,participating}",
  "labels_url": "https://api.github.com/repos/rust-lang/rust/labels{/name}",
  "releases_url": "https://api.github.com/repos/rust-lang/rust/releases{/id}",
  "deployments_url": "https://api.github.com/repos/rust-lang/rust/deployments",
  "created_at": "2010-06-16T20:39:03Z",
  "updated_at": "2026-06-07T15:17:38Z",
  "pushed_at": "2026-06-07T15:02:18Z",
  "git_url": "git://github.com/rust-lang/rust.git",
  "ssh_url": "[email protected]:rust-lang/rust.git",
  "clone_url": "https://github.com/rust-lang/rust.git",
  "svn_url": "https://github.com/rust-lang/rust",
  "homepage": "https://www.rust-lang.org",
  "size": 914819,
  "stargazers_count": 113660,
  "watchers_count": 113660,
  "language": "Rust",
  "has_issues": true,
  "has_projects": true,
  "has_wiki": false,
  "has_pages": false,
  "has_discussions": false,
  "forks_count": 15054,
  "mirror_url": null,
  "archived": false,
  "disabled": false,
  "open_issues_count": 12498,
  "license": {
    "key": "apache-2.0",
    "name": "Apache License 2.0",
    "spdx_id": "Apache-2.0",
    "url": "https://api.github.com/licenses/apache-2.0",
    "node_id": "MDc6TGljZW5zZTI="
  },
  "allow_forking": true,
  "is_template": false,
  "web_commit_signoff_required": false,
  "has_pull_requests": true,
  "pull_request_creation_policy": "all",
  "topics": [
    "compiler",
    "language",
    "rust"
  ],
  "visibility": "public",
  "forks": 15054,
  "open_issues": 12498,
  "watchers": 113660,
  "default_branch": "main",
  "temp_clone_token": null,
  "custom_properties": {

  },
  "organization": {
    "login": "rust-lang",
    "id": 5430905,
    "node_id": "MDEyOk9yZ2FuaXphdGlvbjU0MzA5MDU=",
    "avatar_url": "https://avatars.githubusercontent.com/u/5430905?v=4",
    "gravatar_id": "",
    "url": "https://api.github.com/users/rust-lang",
    "html_url": "https://github.com/rust-lang",
    "followers_url": "https://api.github.com/users/rust-lang/followers",
    "following_url": "https://api.github.com/users/rust-lang/following{/other_user}",
    "gists_url": "https://api.github.com/users/rust-lang/gists{/gist_id}",
    "starred_url": "https://api.github.com/users/rust-lang/starred{/owner}{/repo}",
    "subscriptions_url": "https://api.github.com/users/rust-lang/subscriptions",
    "organizations_url": "https://api.github.com/users/rust-lang/orgs",
    "repos_url": "https://api.github.com/users/rust-lang/repos",
    "events_url": "https://api.github.com/users/rust-lang/events{/privacy}",
    "received_events_url": "https://api.github.com/users/rust-lang/received_events",
    "type": "Organization",
    "user_view_type": "public",
    "site_admin": false
  },
  "network_count": 15054,
  "subscribers_count": 1577
}

All API requests must include a valid User-Agent header. By default, curl sends a valid User-Agent header.

Getting started with the REST API

User-Agentヘッダーが必須である点に注意します。curlは勝手にUser-Agentヘッダーを付けてくれますが、以下のRustコードのように、そうではない場合は明示的に追加します。

プロジェクト作成

cargo new example
cd example

# tree
# .
# ├── Cargo.toml
# └── src
#     └── main.rs
cargo add anyhow
cargo add serde --features derive
cargo add tokio --features macros,rt-multi-thread
cargo add reqwest --features json

例1

src/main.rs
use anyhow::Result;
use reqwest::header::USER_AGENT;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct Repository {
    full_name: String,
    stargazers_count: u64,
    forks_count: u64,
    language: Option<String>,
}

#[tokio::main]
async fn main() -> Result<()> {
    let repo = reqwest::Client::new()
        .get("https://api.github.com/repos/rust-lang/rust")
        .header(USER_AGENT, "example-app")
        .send()
        .await?
        .error_for_status()?
        .json::<Repository>()
        .await?;

    println!("name: {}", repo.full_name);
    println!("stars: {}", repo.stargazers_count);
    println!("forks: {}", repo.forks_count);
    println!(
        "language: {}",
        repo.language.as_deref().unwrap_or("(unknown)")
    );

    Ok(())
}
cargo run
実行結果
name: rust-lang/rust
stars: 113660
forks: 15054
language: Rust

"language": nullを返すリポジトリもあるので(e.g. trueroad/HaranoAjiFonts)、Repository構造体ではlanguageをOptionにしています。

例2

User-Agenttimeoutを設定したreqwest::Clientを再利用できる形にしています。

src/main.rs
use anyhow::{Context, Result};
use reqwest::{
    Client,
    header::{ACCEPT, HeaderMap, HeaderValue, USER_AGENT},
};
use serde::Deserialize;
use std::{env, time::Duration};

#[derive(Debug, Deserialize)]
struct Repository {
    full_name: String,
    stargazers_count: u64,
    forks_count: u64,
    language: Option<String>,
}

fn build_http_client() -> Result<Client> {
    let mut headers = HeaderMap::new();

    headers.insert(USER_AGENT, HeaderValue::from_static("example-app"));

    headers.insert(
        ACCEPT,
        HeaderValue::from_static("application/vnd.github+json"),
    );

    let client = Client::builder()
        .default_headers(headers)
        .timeout(Duration::from_secs(10))
        .build()
        .context("failed to build reqwest client")?;

    Ok(client)
}

async fn fetch_repository(client: &Client, owner: &str, repo: &str) -> Result<Repository> {
    let url = format!("https://api.github.com/repos/{owner}/{repo}");

    let repository = client
        .get(&url)
        .send()
        .await
        .with_context(|| format!("failed to send request: {url}"))?
        .error_for_status()
        .with_context(|| format!("GitHub API returned error status: {url}"))?
        .json::<Repository>()
        .await
        .with_context(|| format!("failed to parse repository JSON: {url}"))?;

    Ok(repository)
}

#[tokio::main]
async fn main() -> Result<()> {
    let owner = env::args()
        .nth(1)
        .unwrap_or_else(|| "rust-lang".to_string());
    let repo = env::args().nth(2).unwrap_or_else(|| "rust".to_string());

    let client = build_http_client()?;
    let repo = fetch_repository(&client, &owner, &repo).await?;

    println!("name: {}", repo.full_name);
    println!("stars: {}", repo.stargazers_count);
    println!("forks: {}", repo.forks_count);
    println!(
        "language: {}",
        repo.language.as_deref().unwrap_or("(unknown)")
    );

    Ok(())
}

# rust-lang/rust
cargo run

# trueroad/HaranoAjiFonts
cargo run -- trueroad HaranoAjiFonts
実行結果
name: trueroad/HaranoAjiFonts
stars: 166
forks: 3
language: (unknown)

Last updated on

目次