For those working with circleCI and GitHub, here is a script we did today
QuickStart
python main.py list-ctx
python main.py list-env
python main.py list-keys
python main.py replace-keys
with the following main.py file
import requests
import argparse
from config import API_TOKEN, GH_TOKEN, ORG # CircleCi api Token, Github api token, Github organisation name
ORG_SLUG = f"github/{ORG}"
auth_header = { 'Circle-Token': API_TOKEN }
github_headers = {
    "X-GitHub-Api-Version": "2022-11-28",
    "Accept": "application/vnd.github+json",
    "Authorization": f"Bearer {GH_TOKEN}"
}
def list_contexts():
    url = "https://circleci.com/api/v2/context"
    req_params = {
        "owner-slug": ORG_SLUG,
        "owner-type": "organization",
    }
    page_token = ""
    contexts = []
    while True:
        params = {**req_params, "page-token": page_token}
        response = requests.get(url, params=params, headers=auth_header).json()
        contexts += response.get("items", [])
        page_token = response.get("next_page_token", None)
        if page_token is None:
            return contexts
def list_context_variables(context_id):
    url = f"https://circleci.com/api/v2/context/{context_id}/environment-variable"
    
    req_params = {}
    page_token = ""
    env_vars = []
    while True:
        params = {**req_params, "page-token": page_token}
        response = requests.get(url, params=params, headers=auth_header).json()
        env_vars += response.get("items", [])
        page_token = response.get("next_page_token", None)
        if page_token is None:
            return env_vars
def list_github_repositories():
    url = f"https://api.github.com/orgs/{ORG}/repos"
    req_params = {
        "per_page": 100
    }
    page_idx = 1
    repos = []
    while True:
        params = {**req_params, "page": page_idx}
        response = requests.get(url, params=params, headers=github_headers).json()
        repos += response
        if len(response) == 0:
            return repos
        page_idx += 1
def list_project_env_variables(project):
    url = f"https://circleci.com/api/v2/project/gh/{ORG}/{project}/envvar"
    
    req_params = {}
    page_token = ""
    env_vars = []
    while True:
        params = {**req_params, "page-token": page_token}
        response = requests.get(url, params=params, headers=auth_header).json()
        env_vars += response.get("items", [])
        page_token = response.get("next_page_token", None)
        if page_token is None:
            return env_vars
def list_project_keys(project):
    url = f"https://circleci.com/api/v2/project/gh/{ORG}/{project}/checkout-key"
    
    req_params = {}
    page_token = ""
    keys = []
    while True:
        params = {**req_params, "page-token": page_token}
        response = requests.get(url, params=params, headers=auth_header).json()
        keys += response.get("items", [])
        page_token = response.get("next_page_token", None)
        if page_token is None:
            return keys 
def list_github_deploy_keys(project):
    url = f"https://api.github.com/repos/{ORG}/{project}/keys"
    req_params = {
        "per_page": 100
    }
    page_idx = 1
    keys = []
    while True:
        params = {**req_params, "page": page_idx}
        response = requests.get(url, params=params, headers=github_headers).json()
        keys += response
        if len(response) == 0:
            return keys
        page_idx += 1
def delete_github_deploy_key(project, key):
    url = f"https://api.github.com/repos/{ORG}/{project}/keys/{key['id']}"
    response = requests.delete(url, headers=github_headers)
    if response.status_code != 204:
        print("Could not delete github key", key['title'], response.json())
def get_project_checkout_keys(project, fingerprint):
    url = f"https://circleci.com/api/v2/project/gh/{ORG}/{project}/checkout-key/{fingerprint}"
    response = requests.get(url, headers=auth_header).json()
    return response
def delete_project_checkout_keys(project, fingerprint):
    url = f"https://circleci.com/api/v2/project/gh/{ORG}/{project}/checkout-key/{fingerprint}"
    response = requests.delete(url, headers=auth_header)
    if response.status_code != 200:
        print("Could not delete circleci key for project", project) 
def create_project_checkout_keys(project, key_type):
    url = f"https://circleci.com/api/v2/project/gh/{ORG}/{project}/checkout-key"
    response = requests.post(url, headers=auth_header, json={"type": key_type})
    if response.status_code != 201:
        print("Could not create circleci key for project", project, response.json()) 
def main_list_project_env_var():
    repos = list_github_repositories()
    print("project;var")
    for repo in repos:
        env_vars = list_project_env_variables(repo["name"])
        for var in env_vars:
            print(f'{repo["name"]};{var["name"]}')
def main_list_project_checkout_keys():
    repos = list_github_repositories()
    print("project;type;fingerprint;created_at")
    for repo in repos:
        keys = list_project_keys(repo["name"])
        for key in keys:
            print(f'{repo["name"]};{key["type"]};{key["fingerprint"]};{key["created_at"]}')
def main_list_contexts():
    contexts = list_contexts()
    print("context;var;last_modified")
    for ctx in contexts:
        env_vars = list_context_variables(ctx["id"])
        for var in env_vars:
            print(f'{ctx["name"]};{var["variable"]};{var["created_at"]}')
def main_replace_project_checkout_keys():
    repos = list_github_repositories()
    for repo in repos:
        project = repo["name"]
        gh_keys = list_github_deploy_keys(project)
        for key in gh_keys:
            delete_github_deploy_key(project, key)
        ci_keys = list_project_keys(project)
        for key in ci_keys:
            delete_project_checkout_keys(project, key["fingerprint"])
            create_project_checkout_keys(project, key_type=key['type'])
            
command_dict = {
    "list-ctx": main_list_contexts,
    "list-env": main_list_project_env_var,
    "list-keys": main_list_project_checkout_keys,
    "replace-keys": main_replace_project_checkout_keys
}
if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("cmd", choices=list(command_dict.keys()))
    args = parser.parse_args()
    
    command_dict[args.cmd]()