403 error when trying to download filing

I am running the script displayed here:

https://github.com/LSEG-API-Samples/Example.RDPAPI.Python.IntroductionToFilings/blob/main/IntroToFilings.ipynb

It works fine until:

# Define Helper Function retrieveURL

def retrieveURL(token, retrievalParameters):

ENDPOINT_DOC_RETRIEVAL = RDP_BASE_URL+'/data/filings'+RDP_FILINGS_VERSION + '/retrieval/search/' + retrievalParameters

headers = {

"Authorization": "Bearer " + token,

"X-API-Key": "155d9dbf-f0ac-46d9-8b77-f7f6dcd238f8",

"ClientID" : "api_playground"

}

...

# Retrieve URL by FilingId

jsonFullResp = retrieveURL(accessToken,'filingId/97661417885')


where I get:

Next we retrieve: https://api.refinitiv.com/data/filings/v1/retrieval/search/filingId/97661417885

Response status code =403


Questions:

1) Why does the author of that post set the ClientID to api_playground? My understanding of GraphQL playgrounds is that they serve to try things and to understand data structure, not to launch downloads of filings via systematic API access.

2) Why do I get the 403 error? I also tried substituting the X-API-Key and ClientID with my credentials that worked well further up the code when I was obtaining the token, but same result.


Thank you.

Best Answer

  • Gurpreet
    Answer ✓

    Hello @user2,

    Please try to use the filings Python and Postman sample in the downloads tab of developers portal. The Python sample shows the complete GraphQL search and subsequent download and saving of the file. You can read about the structure of filings request in the document.

    The client ID used in the filings request header is just a unique identified and can be any text that specifically identifies your application name.

Answers

  • Dear Gurpreet,

    Thank you very much.

    I ran the filings.py. Before that, I dumped my username, password and appKey as v1 authentication parameters in the credential file.

    Executing the code from filings.py yields no error (authentification seems to work) but a docId that is a NoneType object. I would have expected something different. I also tried the other example from the filings.py file - same result.

    Best

    Thomas


    Here the code, it is 1:1 copy-paste from the sample file:

    #=============================================================================

    # Refinitiv Data Platform demo app to get filings data

    #-----------------------------------------------------------------------------

    # This source code is provided under the Apache 2.0 license

    # and is provided AS IS with no warranty or guarantee of fit for purpose.

    # Copyright (C) 2021 Refinitiv. All rights reserved.

    #=============================================================================

    import requests

    import json

    import rdpToken


    # Application Constants

    RDP_version = "/v1"

    base_URL = "https://api.refinitiv.com"

    DOC_CLIENT_ID = "my_application_name"

    DOC_API_KEY = "155d9dbf-f0ac-46d9-8b77-f7f6dcd238f8"


    #==============================================

    def requestSearch(searchPayload):

    #==============================================

    category_URL = "/data-store"

    endpoint_URL = "/graphql"

    RESOURCE_ENDPOINT = base_URL + category_URL + RDP_version + endpoint_URL

    requestData = {

    "query": searchPayload

    }


    # get the latest access token

    accessToken = rdpToken.getToken()

    hdrs = {

    'Authorization': "Bearer " + accessToken,

    'Content-Type': "application/json",

    'cache-control': "no-cache"

    }


    sResp = requests.post(RESOURCE_ENDPOINT, headers=hdrs, data = json.dumps(requestData))


    if sResp.status_code != 200:

    raise ValueError("Unable to search. Code %s, Message: %s" % (sResp.status_code, sResp.text))

    else:

    jResp = json.loads(sResp.text)

    documentID = jResp["data"]["FinancialFiling"][0]["FilingDocument"]["DocId"]

    return documentID



    #==============================================

    def retrieveDocURL(documentID):

    #==============================================

    category_URL = "/data/filings/"

    endpoint_URL = "/retrieval/search/docId/"

    RESOURCE_ENDPOINT = base_URL + category_URL + RDP_version + endpoint_URL + documentID


    # get the latest access token

    accessToken = rdpToken.getToken()

    hdrs = {

    'Authorization': "Bearer " + accessToken,

    "X-API-Key": DOC_API_KEY,

    "ClientID" : DOC_CLIENT_ID

    }


    rResp = requests.get(RESOURCE_ENDPOINT, headers = hdrs)


    if rResp.status_code != 200:

    raise ValueError("Unable to get document URL. Code %s, Message: %s" % (rResp.status_code, rResp.text))

    else:

    jResp = json.loads(rResp.text)

    fName = list(jResp.keys())[0]

    sURL = jResp[list(jResp.keys())[0]]["signedUrl"]

    return fName, sURL



    #==============================================

    def retrieveSaveDoc(fileName, signedUrl):

    #==============================================

    dResp = requests.get(signedUrl, allow_redirects=True)


    if dResp.status_code != 200:

    raise ValueError("Unable to download the document. Response: " % (dResp.status_code, dResp.text))

    else:

    with open(fileName, 'wb') as f:

    f.write(dResp.content)

    f.close()

    print("The document [%s], has been downloaded" % fileName)




    #==============================================

    if __name__ == "__main__":

    #==============================================

    documentSearchText = """

    {

    FinancialFiling(

    sort: {FilingDocument: {DocumentSummary: {FilingDate: DESC}}},

    filter: {FilingDocument: {DocumentSummary: {FilingDate: {BETWN: {FROM: "2020-07-01T00:00:00Z", TO: "2020-08-01T00:00:00Z"}}}}},

    keywords: {searchstring: "FinancialFiling.FilingDocument.DocumentText:COVID-19"},

    limit: 5) {

    _metadata {

    totalCount

    }

    FilingOrganization {

    Names {

    Name {

    OrganizationName(

    filter: {AND: [ {

    OrganizationNameLanguageId: {EQ: "505062"}}, {

    OrganizationNameTypeCode: {EQ: "LNG"}}]})

    {

    OrganizationName

    }

    }

    }

    }

    FilingDocument {

    DocId

    DocumentSummary {

    DocumentTitle

    FilingDate

    FormType

    FeedName

    }

    DocumentText

    }

    }

    }

    """


    # search for the document first

    print("Performing a document search using document-text...")

    docId = requestSearch(documentSearchText)

  • Hi @user2,

    Do you have access to the filings API. I just executed the sample without any modification, and am able to download the file:

    >> python filings.py
    Performing a document search using document-text...
    Read credentials from file
    Getting a new token using Password Grant...
    Saving the new token
    Document ID is: 54938820

    Retrieving the document URL for this DocID...
    Existing token read from: token.txt
    Document fileName is: 20200801_5000601244_97659617443_1_12_INTRM_raw.pdf
    Retrieval signedUrl is: https://*****

    Downloading the document: 20200801_5000601244_97659617443_1_12_INTRM_raw.pdf...
    The document [20200801_5000601244_97659617443_1_12_INTRM_raw.pdf], has been downloaded


    For the OAuth access token that you get - does it have filings related scope associated with it. Namely -

    trapi.data.filings.metadata
    trapi.data.filings.retrieval
    trapi.data.filings.search


  • Dear Gurpreet,


    Thank you. It seems that the token does not have these features, see below.


    How can I extend the scope to filings?

    Best

    Thomas

    "scope": "trapi.alerts.history.crud trapi.alerts.preferences.crud trapi.alerts.publication.crud trapi.alerts.subscription.crud trapi.auth.cloud-credentials trapi.cfs.claimcheck.read trapi.data.average-volume-analytics.ava_read trapi.data.benchmark.bmk_read trapi.data.get.data.read trapi.data.historical-pricing.events.read trapi.data.historical-pricing.summaries.read trapi.data.quantitative-analytics.read trapi.data.symbology.advanced.read trapi.data.symbology.read trapi.frtb.sentimarization trapi.graphql.subscriber.access trapi.messenger trapi.metadata.read trapi.sdbold trapi.search.explore.read trapi.search.lookup.read trapi.search.metadata.read trapi.search.read trapi.searchcore.lookup.read trapi.searchcore.metadata.read trapi.searchcore.read trapi.streaming.prcperf.read trapi.streaming.synthetic.read trapi.synthetic.crud trapi.user-framework.application-metadata.raplib trapi.user-framework.mobile.crud trapi.user-framework.recently-used.crud trapi.user-framework.workspace.crud trapi.userdata.portfolio-management.read"

  • wasin.w
    wasin.w admin
    Hello @user2

    Please contact your LSEG representative to help you with the RDP Filing service permission.

  • Dear Gurpreet,

    It seems that my token does not have these features, see below. How can I extend the scope to filings?

    "scope": "trapi.alerts.history.crud trapi.alerts.preferences.crud trapi.alerts.publication.crud trapi.alerts.subscription.crud trapi.auth.cloud-credentials trapi.cfs.claimcheck.read trapi.data.average-volume-analytics.ava_read trapi.data.benchmark.bmk_read trapi.data.get.data.read trapi.data.historical-pricing.events.read trapi.data.historical-pricing.summaries.read trapi.data.quantitative-analytics.read trapi.data.symbology.advanced.read trapi.data.symbology.read trapi.frtb.sentimarization trapi.graphql.subscriber.access trapi.messenger trapi.metadata.read trapi.sdbold trapi.search.explore.read trapi.search.lookup.read trapi.search.metadata.read trapi.search.read trapi.searchcore.lookup.read trapi.searchcore.metadata.read trapi.searchcore.read trapi.streaming.prcperf.read trapi.streaming.synthetic.read trapi.synthetic.crud trapi.user-framework.application-metadata.raplib trapi.user-framework.mobile.crud trapi.user-framework.recently-used.crud trapi.user-framework.workspace.crud trapi.userdata.portfolio-management.read"

  • @user2

    Please contact your LSEG representative to help you with the RDP Filing service permission.