How to fetch opportunities from Microsoft Dynamics 365 Sales with Python

Microsoft Dynamics 365 Sales offers a robust customer relationship management (CRM) system, enabling businesses to understand their customers' needs more accurately, engage accounts more intelligently, and secure more deals.

While there's a variety of data worth pulling from Microsoft Dynamics 365 Sales, opportunities are likely at or near the top of your list.

To help you get opportunities from the CRM in Python, we'll breakdown each step you need to follow.

Authenticating with Microsoft Dynamics 365 Sales API

To interact with the Microsoft Dynamics 365 Sales API, you will need to authenticate your application using OAuth 2.0. The authentication process follows the Authorization Code grant type and involves several steps:

  1. Set up an OAuth flow with Microsoft Dynamics 365 Sales: After this setup, direct your users to the authorization URL, which is <code class="blog_inline-code">https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize</code>. The users need to authorize from this URL.
  2. Redirect URL and Authorization Code: Upon successful authorization, Microsoft Dynamics 365 Sales will navigate the user back to the <code class="blog_inline-code">redirect_url</code> you provided. Included in the redirection will be the <code class="blog_inline-code">code</code> as a query parameter. This is the authorization code that you will later exchange for an access token.
  3. Scopes: Include the <code class="blog_inline-code">https://{DOMAIN}.dynamics.com/.default offline_access</code> scopes in your request. Scopes define the level of access that your application requires.
  4. Accessing the Token URL: Use the client ID and secret provided by Microsoft Dynamics 365 Sales to hit the token URL <code class="blog_inline-code">https://login.microsoftonline.com/organizations/oauth2/v2.0/token</code> and request an access token. The <code class="blog_inline-code">client_id</code>, <code class="blog_inline-code">client_secret</code>, and other parameters should be passed into the URL as body parameters.
  5. Header Format: Make sure to use the <code class="blog_inline-code">Content-Type: application/x-www-form-urlencoded</code> header format for the token URL.
  6. Accessing the Access Token: The access token will be made available in the <code class="blog_inline-code">access_token</code> key in the response from the token URL.
  7. Token Expiry: You can find the validity of the token in the expires_in value in the response body of the token URL.
  8. Refresh Token: The refresh token will be available in the refresh_token key in the response. This token can be used to refresh your access token when it expires.

By following the steps above, you can successfully authenticate and interact with the Microsoft Dynamics 365 Sales API.

python import requests from requests.auth import HTTPBasicAuth import json

You will need to use your own <code class="blog_inline-code">client_id</code> and <code class="blog_inline-code">client_secret</code>.

Replace {DOMAIN} with your Microsoft Dynamics 365 Domain

CLIENT_ID = 'your_client_id'
CLIENT_SECRET = 'your_client_secret'
REDIRECT_URI = 'your_redirect_uri'
TOKEN_URL = 'https://login.microsoftonline.com/organizations/oauth2/v2.0/token'
AUTH_URL = 'https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize'
SCOPE = 'https://{DOMAIN}.dynamics.com/.default offline_access'
API_URL = 'https://{DOMAIN}.dynamics.com/api/data/v9.0/opportunities'

Get the Authorization Code

auth_response = requests.get(
    AUTH_URL,
    params={
        'client_id': CLIENT_ID,
        'response_type': 'code',
        'redirect_uri': REDIRECT_URI,
        'scope': SCOPE
    }
)

Extract Authorization Code

auth_code = auth_response.json()['code']

Get the Access Token

token_response = requests.post(
    TOKEN_URL,
    headers={
        'Content-Type': 'application/x-www-form-urlencoded'
    },
    data={
        'client_id': CLIENT_ID,
        'client_secret': CLIENT_SECRET,
        'code': auth_code,
        'redirect_uri': REDIRECT_URI,
        'grant_type': 'authorization_code'
    }
)

Extract Access Token

access_token = token_response.json()['access_token']

Extract Refresh Token

refresh_token = token_response.json()['refresh_token']

Fetch Opportunities

api_response = requests.get(
    API_URL,
    headers={
        'Authorization': f'Bearer {access_token}'
    }
)

Print Opportunities

print(json.dumps(api_response.json(), indent=4))

This script initiates the OAuth 2 flow by fetching an authorization code, exchanging it for an access token, and then using that access token to authenticate a request to the Microsoft Dynamics 365 Sales API. The script prints out the JSON response from the API, which includes the Opportunities data.

You should see the list of Opportunities from Microsoft Dynamics 365 Sales as the output.

{
    "@odata.context": "https://services.odata.org/V4/(S(sapj4y0e5v))/TripPinServiceRW/",
    "value": [
        {
            "@odata.etag": "W/\"08D7D40891852C0F\"",
            "prioritycode": 1,
            "completeinternalreview": true,
            "stepname": "Step 1",
            "filedebrief": false,
            "estimatedclosedate": "2022-12-25",
            "_pricelevelid_value": "8f5c9e9f-3bb0-4ce9-851c-2f538c8b1b27",
            "totalamount": 5000,
            "confirminterest": true,
            "captureproposalfeedback": false,
            "exchangerate": 1.3,
            "opportunityid": "d9a9a9da-1daa-4daa-adaa-2daa3daa4daa",
            "_parentcontactid_value": "a1b2c3d4-e5f6-a7b8-c9d0-e1f2a3b4c5d6",
            "identifycompetitors": true,
            "_parentaccountid_value": "b1c2d3e4-f5a6-b7c8-d9e0-b1c2d3e4f5a6",
            "name": "New Opportunity",
            "decisionmaker": false,
            "totallineitemamount": 4500,
            "isrevenuesystemcalculated": true,
            "modifiedon": "2022-02-15T18:25:43Z",
            "_owninguser_value": "c1d2e3f4-a5b6-c7d8-e9f0-c1d2e3f4a5b6",
            "skippricecalculation": 0,
            "presentproposal": true,
            "proposedsolution": "Proposed Solution Details",
            "totaldiscountamount": 500,
            "_ownerid_value": "d1e2f3a4-b5c6-d7e8-f9a0-d1e2f3a4b5c6",
            "sendthankyounote": true,
            "identifycustomercontacts": true,
            "evaluatefit": true,
            "totalamountlessfreight": 4800,
            "totallineitemdiscountamount": 200,
            "totalamountlessfreight_base": 4800,
            "msdyn_gdproptout": false,
            "statuscode": 1,
            "createdon": "2022-02-15T18:25:43Z",
            "versionnumber": 1,
            "emailaddress": "contact@example.com",
            "customerneed": "Customer needs details",
            "_msdyn_predictivescoreid_value": "e1f2a3b4-c5d6-e7f8-a9b0-c1d2e3f4a5b6",
            "totaltax_base": 100,
            "totallineitemamount_base": 4500,
            "estimatedvalue": 5000,
            "totalamount_base": 5000,
            "developproposal": true,
            "purchaseprocess": 1,
            "description": "Opportunity description",
            "_msdyn_opportunitykpiid_value": "f1a2b3c4-d5e6-f7a8-b9c0-d1e2f3a4b5c6",
            "resolvefeedback": true,
            "totaltax": 100,
            "totaldiscountamount_base": 500,
            "_transactioncurrencyid_value": "g1h2i3j4-k5l6-g7h8-i9j0-g1h2i3j4k5l6",
            "estimatedvalue_base": 5000,
            "msdyn_forecastcategory": 1,
            "_modifiedby_value": "h1i2j3k4-l5m6-h7i8-j9k0-h1i2j3k4l5m6",
            "presentfinalproposal": true,
            "_createdby_value": "i1j2k3l4-m5n6-i7j8-k9l0-i1j2k3l4m5n6",
            "timezoneruleversionnumber": 4,
            "currentsituation": "Current situation details",
            "pricingerrorcode": 0,
            "salesstagecode": 1,
            "totallineitemdiscountamount_base": 200,
            "purchasetimeframe": 1,
            "identifypursuitteam": true,
            "closeprobability": 0.8,
            "participatesinworkflow": false,
            "statecode": 0,
            "_owningbusinessunit_value": "j1k2l3m4-n5o6-j7k8-l9m0-j1k2l3m4n5o6",
            "pursuitdecision": true,
            "opportunityratingcode": 1,
            "_customerid_value": "k1l2m3n4-o5p6-k7l8-m9n0-k1l2m3n4o5p6",
            "completefinalproposal": true,
            "msdyn_opportunityscore": null,
            "msdyn_scorehistory": null,
            "actualvalue_base": 5000,
            "msdyn_opportunityscoretrend": null,
            "_msdyn_segmentid_value": null,
            "budgetstatus": null,
            "_accountid_value": null,
            "finaldecisiondate": "2022-12-25",
            "msdyn_similaropportunities": null,
            "_slainvokedid_value": null,
            "msdyn_opportunitygrade": null,
            "quotecomments": null,
            "timeline": null,
            "qualificationcomments": null,
            "_contactid_value": null,
            "_campaignid_value": null,
            "stageid": null,
            "lastonholdtime": null,
            "need": null,
            "stepid": null,
            "processid": null,
            "onholdtime": null,
            "freightamount": null,
            "discountamount": null,
            "discountpercentage": null,
            "_owningteam_value": null,
            "_slaid_value": null,
            "freightamount_base": null,
            "msdyn_scorereasons": null,
            "discountamount_base": null,
            "overriddencreatedon": null,
            "teamsfollowed": null,
            "traversedpath": null,
            "importsequencenumber": null,
            "initialcommunication": null,
            "schedulefollowup_qualify": null,
            "_createdonbehalfby_value": null,
            "_originatingleadid_value": null,
            "customerpainpoints": null,
            "scheduleproposalmeeting": null,
            "schedulefollowup_prospect": null,
            "utcconversiontimezonecode": null,
            "timespentbymeonemailandmeetings": null
        }
    ]
}

Related: Benefits of marketing automation integration

Final thoughts

It's highly probable that your clients utilize a diverse range of CRM systems—not just Microsoft Dynamics 365 Sales.

To cater to this varied landscape of CRM tools, you can build to Merge's CRM Unified API. Once you’ve built to it, you can offer dozens of CRM integrations to clients, whether that’s Salesforce, HubSpot, ActiveCampaign, etc.

You can learn more about Merge by scheduling a demo with one of our integration experts.