{"id":548,"date":"2025-10-26T22:53:23","date_gmt":"2025-10-26T22:53:23","guid":{"rendered":"https:\/\/ykim.synology.me\/wordpress\/?p=548"},"modified":"2025-10-26T22:53:28","modified_gmt":"2025-10-26T22:53:28","slug":"how-to-create-a-job-in-the-youtube-studio-reports-section-or-via-the-api","status":"publish","type":"post","link":"https:\/\/ykim.synology.me\/wordpress\/how-to-create-a-job-in-the-youtube-studio-reports-section-or-via-the-api-548\/","title":{"rendered":"How to create a job in the YouTube Studio &#8216;Reports&#8217; section or via the API"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">You can create a YouTube Reporting job (which tells YouTube to generate daily bulk data reports for you) either through the API or, for certain system-managed reports, they might be automatically available via the Studio interface.<sup>1<\/sup><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Method 1: Via the YouTube Studio &#8216;Reports&#8217; Section<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Currently, <strong>you cannot manually create <em>new<\/em> custom Reporting API jobs directly within the standard YouTube Studio interface.<\/strong> The Studio interface focuses on the <em>Analytics<\/em> API (visual dashboards, customizable charts, exporting the current view) rather than scheduling bulk CSV downloads via the <em>Reporting<\/em> API.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">However, if you are a <strong>Content Owner<\/strong> (usually managing multiple channels or dealing with Content ID), YouTube automatically generates certain &#8220;System-Managed Reports&#8221; for you.<sup>2<\/sup> These cover things like ad revenue and asset performance.<sup>3<\/sup><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>To access and download these pre-generated System-Managed Reports:<\/strong><\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li>Sign in to <strong>YouTube Studio<\/strong>.<\/li>\n\n\n\n<li>Make sure you are viewing as the <strong>Content Owner<\/strong> (you might need to switch accounts using your profile picture in the top right).<\/li>\n\n\n\n<li>From the left menu, select <strong>Reports<\/strong>. \ud83d\udcc4<\/li>\n\n\n\n<li>At the top of the page, select the <strong>type<\/strong> of report you want (e.g., Financial Summary, Asset Performance).<\/li>\n\n\n\n<li>Choose the <strong>frequency<\/strong> (Weekly or Monthly) and the <strong>version<\/strong> of the report.<\/li>\n\n\n\n<li>Click the report name for the desired date range to download the CSV file.<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Method 2: Via the YouTube Reporting API (Using Python)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This is the standard way to create <em>custom<\/em> reporting jobs for various data types (like daily video views, demographics, traffic sources, etc.).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Prerequisites:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Python installed.<sup>4<\/sup><\/li>\n\n\n\n<li>Google API Client Library for Python installed:Bash<code>pip install google-api-python-client google-auth-oauthlib google-auth<\/code><\/li>\n\n\n\n<li><code>client_secrets.json<\/code> file downloaded from your Google Cloud project (with YouTube Reporting API enabled).<\/li>\n\n\n\n<li>OAuth 2.0 authentication handled (the script below includes this).<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Python Code (<code>create_job.py<\/code>):<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Python<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nimport os\nimport pickle\nimport google_auth_oauthlib.flow\nimport googleapiclient.discovery\nimport googleapiclient.errors\nfrom google.auth.transport.requests import Request\n\n# --- CONFIGURATION ---\n# Need read\/write scope to create jobs\nSCOPES = &#x5B;&#039;https:\/\/www.googleapis.com\/auth\/yt-analytics.readonly&#039;]\nAPI_SERVICE_NAME = &#039;youtubereporting&#039;\nAPI_VERSION = &#039;v1&#039;\nCLIENT_SECRETS_FILE = &#039;client_secrets.json&#039;\nTOKEN_PICKLE_FILE = &#039;token.pickle&#039; # Same file as the download script\n\n# --- AUTHENTICATION ---\ndef get_authenticated_service():\n    &quot;&quot;&quot;Handles OAuth 2.0 flow and returns an authenticated API service.&quot;&quot;&quot;\n    creds = None\n    if os.path.exists(TOKEN_PICKLE_FILE):\n        with open(TOKEN_PICKLE_FILE, &#039;rb&#039;) as token:\n            creds = pickle.load(token)\n\n    if not creds or not creds.valid:\n        if creds and creds.expired and creds.refresh_token:\n            try:\n                creds.refresh(Request())\n            except Exception as e:\n                 print(f&quot;Error refreshing token: {e}. Re-authenticating.&quot;)\n                 creds = None # Force re-authentication if refresh fails\n\n        if not creds: # Need to authenticate\n            if not os.path.exists(CLIENT_SECRETS_FILE):\n                print(f&quot;Error: {CLIENT_SECRETS_FILE} not found.&quot;)\n                exit()\n            flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file(\n                CLIENT_SECRETS_FILE, SCOPES)\n            creds = flow.run_local_server(port=0)\n\n        with open(TOKEN_PICKLE_FILE, &#039;wb&#039;) as token:\n            pickle.dump(creds, token)\n\n    return googleapiclient.discovery.build(\n        API_SERVICE_NAME, API_VERSION, credentials=creds)\n\n# --- List Available Report Types ---\ndef list_report_types(service):\n    &quot;&quot;&quot;Lists available report types.&quot;&quot;&quot;\n    print(&quot;Fetching available report types...&quot;)\n    try:\n        results = service.reportTypes().list().execute()\n        report_types = results.get(&#039;reportTypes&#039;, &#x5B;])\n\n        if not report_types:\n            print(&quot;No report types found.&quot;)\n            return None\n        else:\n            print(&quot;Available Report Types:&quot;)\n            for i, r_type in enumerate(report_types):\n                print(f&quot;  &#x5B;{i+1}] ID: {r_type&#x5B;&#039;id&#039;]}&quot;)\n                print(f&quot;      Name: {r_type&#x5B;&#039;name&#039;]}&quot;)\n            return report_types\n    except googleapiclient.errors.HttpError as e:\n        print(f&quot;An API error occurred: {e}&quot;)\n        return None\n\n\n# --- Create Job Function ---\ndef create_reporting_job(service, report_type_id, job_name):\n    &quot;&quot;&quot;Creates a new reporting job.&quot;&quot;&quot;\n    print(f&quot;\\nCreating job &#039;{job_name}&#039; for report type &#039;{report_type_id}&#039;...&quot;)\n    job_body = {\n        &#039;reportTypeId&#039;: report_type_id,\n        &#039;name&#039;: job_name\n    }\n    try:\n        response = service.jobs().create(body=job_body).execute()\n        print(&quot;\\n--- Job Created Successfully ---&quot;)\n        print(f&quot;Job ID: {response&#x5B;&#039;id&#039;]}&quot;)\n        print(f&quot;Job Name: {response&#x5B;&#039;name&#039;]}&quot;)\n        print(f&quot;Report Type ID: {response&#x5B;&#039;reportTypeId&#039;]}&quot;)\n        print(f&quot;Create Time: {response&#x5B;&#039;createTime&#039;]}&quot;)\n        print(&quot;--------------------------------&quot;)\n        print(&quot;Reports will start generating within 48 hours.&quot;)\n        return response\n    except googleapiclient.errors.HttpError as e:\n        print(f&quot;An API error occurred: {e}&quot;)\n        error_details = e.resp.get(&#039;content&#039;, b&#039;&#039;).decode(&#039;utf-8&#039;)\n        if &quot;jobs.create&quot; in str(e) and &quot;409&quot; in str(e): # 409 Conflict means it likely already exists\n             print(&quot;Error: A job with this reportTypeId might already exist for your channel.&quot;)\n        elif &quot;403&quot; in str(e) and &quot;system-managed&quot; in error_details.lower():\n             print(&quot;Error: Cannot create jobs for system-managed report types.&quot;)\n        else:\n             print(f&quot;Details: {error_details}&quot;)\n        return None\n\n# --- MAIN EXECUTION ---\ndef main():\n    service = get_authenticated_service()\n\n    available_types = list_report_types(service)\n    if not available_types:\n        return\n\n    # --- User Input ---\n    while True:\n        try:\n            choice = int(input(f&quot;Enter the number of the report type you want to create a job for (1-{len(available_types)}): &quot;))\n            if 1 &lt;= choice &lt;= len(available_types):\n                selected_report_type = available_types&#x5B;choice-1]\n                break\n            else:\n                print(&quot;Invalid choice.&quot;)\n        except ValueError:\n            print(&quot;Please enter a number.&quot;)\n\n    job_name_input = input(f&quot;Enter a name for this job (e.g., &#039;Daily {selected_report_type&#x5B;&#039;name&#039;]}&#039;): &quot;)\n    if not job_name_input:\n         job_name_input = f&quot;My {selected_report_type&#x5B;&#039;name&#039;]} Job&quot; # Default name\n\n    # --- Create the Job ---\n    create_reporting_job(service, selected_report_type&#x5B;&#039;id&#039;], job_name_input)\n\nif __name__ == &#039;__main__&#039;:\n    main()\n\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\"><strong>How to Use the API Script:<\/strong><\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li>Save the code as <code>create_job.py<\/code>.<sup>5<\/sup><\/li>\n\n\n\n<li>Make sure <code>client_secrets.json<\/code> is in the same folder.<\/li>\n\n\n\n<li>Run it: <code>python create_job.py<\/code><\/li>\n\n\n\n<li>It will first authenticate (if needed).<\/li>\n\n\n\n<li>Then, it lists the available report types you can create jobs for (e.g., <code>channel_basic_a2<\/code>, <code>channel_traffic_source_a2<\/code>, etc.).<\/li>\n\n\n\n<li>It prompts you to enter the number corresponding to the report type you want.<\/li>\n\n\n\n<li>It prompts you to enter a name for the job.<\/li>\n\n\n\n<li>It calls the <code>jobs.create<\/code> method and confirms if the job was created successfully.<sup>6<\/sup><\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Once created, YouTube will start generating daily CSV reports for that job, which you can then download using the <em>other<\/em> script (from the previous answer). Remember, it takes up to 48 hours for the first report to appear.<sup>7<\/sup><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n<div style='text-align:center' class='yasr-auto-insert-overall'><\/div><div style='text-align:center' class='yasr-auto-insert-visitor'><\/div>","protected":false},"excerpt":{"rendered":"<p>You can create a YouTube Reporting job (which tells YouTube to generate daily bulk data reports for you) either through the API or, for certain system-managed reports, they might be automatically available via the Studio interface.1 Method 1: Via the YouTube Studio &#8216;Reports&#8217; Section Currently, you cannot manually create new custom Reporting API jobs directly&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"_kadence_starter_templates_imported_post":false,"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","yasr_overall_rating":0,"yasr_post_is_review":"","yasr_auto_insert_disabled":"","yasr_review_type":"","fifu_image_url":"","fifu_image_alt":"","iawp_total_views":0,"footnotes":""},"categories":[55,10],"tags":[],"class_list":["post-548","post","type-post","status-publish","format-standard","hentry","category-application-slug","category-software-slug"],"yasr_visitor_votes":{"stars_attributes":{"read_only":false,"span_bottom":false},"number_of_votes":0,"sum_votes":0},"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/ykim.synology.me\/wordpress\/wp-json\/wp\/v2\/posts\/548","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ykim.synology.me\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ykim.synology.me\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ykim.synology.me\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ykim.synology.me\/wordpress\/wp-json\/wp\/v2\/comments?post=548"}],"version-history":[{"count":1,"href":"https:\/\/ykim.synology.me\/wordpress\/wp-json\/wp\/v2\/posts\/548\/revisions"}],"predecessor-version":[{"id":549,"href":"https:\/\/ykim.synology.me\/wordpress\/wp-json\/wp\/v2\/posts\/548\/revisions\/549"}],"wp:attachment":[{"href":"https:\/\/ykim.synology.me\/wordpress\/wp-json\/wp\/v2\/media?parent=548"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ykim.synology.me\/wordpress\/wp-json\/wp\/v2\/categories?post=548"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ykim.synology.me\/wordpress\/wp-json\/wp\/v2\/tags?post=548"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}