Query the github api and filter the last 1000 commits which include and exclude certain words from the top 3 repositories in a given organization
if request.method == 'POST': is redundant; delete it.
Your

your request is missing the 'org', 'include' or 'ignore' parameters

is less than helpful. Why not tell the user exactly what they're missing?
This except block:
    try:
        org = request.get_json()[&quot;org&quot;]
        include = request.get_json()[&quot;include&quot;]
        word_to_ignore = request.get_json()[&quot;ignore&quot;]

    except:
        return {&quot;error&quot;: &quot;your request is missing the 'org', 'include' or 'ignore' parameters&quot;}

will tell the user that their request is missing a parameter even if the server runs out of memory, or the entire payload is the string banana instead of a well-formed JSON body; or someone attaches the server process to a console and presses Ctrl+C to break it. Are you sure that all three of these exceptions constitute the user having submitted a well-formed JSON payload missing one or more keys? Never bare except:.
Move your query string parameters into a params dictionary sent to requests.
This:
    if not &quot;items&quot; in repo_data_dict:
        if &quot;errors&quot; in repo_data_dict:
            return {&quot;invalid_organization&quot;: f&quot;{org} does not exist or doesn't have any repositories&quot;}
        else:
            return {&quot;reached_api_limit&quot;: True}

is a wild and crazy guess. What if 'errors' told you that the server is down for maintenance, or you submitted a malformed payload, or the endpoint is deprecated? You need to pay attention to the contents of the error message given to you.
Don't json.loads here. Just call .json() on the response.
Be sure to check for HTTP error conditions via response.raise_for_status().
You need to break up get_matches into multiple methods.
You need to improve separation of concerns - get_matches returns dictionaries intended for consumption by the REST API, but that shouldn't be done in that function - payload dictionary formation should be done above, in the Flask handler function. To indicate errors, you can throw your own exceptions, catch them in the method above and add that to the response dictionary. To indicate whether you hit the API limit, you can add a boolean to your return tuple.
total_count is unused; delete it.
Your response format is flat, and this can be inconvenient and inefficient to consume on the client side. Instead consider a dictionary of repositories, each value being a list of messages, rather than adding the repo name to every response element.
This check:
                    if commits[&quot;total_count&quot;] &lt; MAX_PER_PAGE:
                        items_exist = False

seems to exist at the wrong level of indentation, since it applies to commits in general and not the current iterated commit.
This:
commits[&quot;total_count&quot;] &gt; page-1 * MAX_PER_PAGE

almost certainly does not do what you want, since it's doing
page - (1*MAX_PER_PAGE)

and not
(page - 1)*MAX_PER_PAGE

so add parens as appropriate.
Some of the above suggestions baked into an example:
from pprint import pprint
from typing import List, Tuple

import requests

NUM_REPOS = 3
MAX_PER_PAGE = 100
NUM_PAGES = 10
HEADERS = {&quot;Accept&quot;: &quot;application/vnd.github.cloak-preview&quot;}


class InvalidOrganizationError(Exception):
    pass


class APILimitError(Exception):
    pass


# @app.route('/search', methods=('POST',))
def handle_search() -&gt; dict:
    payload = request.get_json()

    try:
        org = payload['org']
    except KeyError:
        return {'error': 'Missing the &quot;org&quot; parameter'}

    try:
        include = payload['include']
    except KeyError:
        return {'error': 'Missing the &quot;include&quot; parameter'}

    try:
        word_to_ignore = payload['word_to_ignore']
    except KeyError:
        return {'error': 'Missing the &quot;word_to_ignore&quot; parameter'}

    matches, api_limited = get_matches(org, include, word_to_ignore)
    return {'matches': matches, 'reached_api_limit': api_limited}


def get_repo(org: str) -&gt; dict:
    with requests.get(
        'https://api.github.com/search/repositories',
        params={
            'q': f'org:{org}',
            'sort': 'stars',
            'order': 'desc',
            'per_page': NUM_REPOS,
        },
        headers=HEADERS,
    ) as resp:
        resp.raise_for_status()
        repo_data_dict = resp.json()

    repo_list = repo_data_dict.get('items')
    if repo_list is not None:
        return repo_list

    if 'errors' in repo_data_dict:
        raise InvalidOrganizationError(
            f'{org} does not exist or does not have any repositories'
        )
    raise APILimitError()


def get_commits(org: str, include: str, repo_name: str, page: int) -&gt; dict:
    with requests.get(
        'https://api.github.com/search/commits',
        params={
            'q': f'{include} repo:{org}/{repo_name}',
            'per_page': MAX_PER_PAGE,
            'page': page,
        },
        headers=HEADERS,
    ) as resp:
        resp.raise_for_status()
        return resp.json()


def get_matches(
    org: str, include: str, word_to_ignore: str,
) -&gt; Tuple[
    List[object],  # All matches
    bool,          # Did we hit the API limit?
]:
    repo_list = get_repo(org)

    matches = []

    for repo in repo_list:
        messages = []
        repo_info = {'repo': repo['name'], 'messages': messages}
        matches.append(repo_info)

        page = 1
        items_exist = True

        while items_exist and page &lt;= NUM_PAGES:
            commits = get_commits(org, include, repo['name'], page)

            if &quot;message&quot; in commits:
                return matches, True

            if &quot;items&quot; in commits and (
                page == 1 or commits[&quot;total_count&quot;] &gt; (page-1) * MAX_PER_PAGE
            ):
                for commit in commits[&quot;items&quot;]:
                    message = commit[&quot;commit&quot;][&quot;message&quot;]

                    if word_to_ignore in message:
                        continue

                    messages.append(message)

            else:
                break

            if commits[&quot;total_count&quot;] &lt; MAX_PER_PAGE:
                items_exist = False
                
            page += 1

    return matches, False


def test():
    matches, api_limited = get_matches('microsoft', 'repo', 'fetch')
    pprint(matches)


if __name__ == '__main__':
    test()