linkedin-api icon indicating copy to clipboard operation
linkedin-api copied to clipboard

Send In Mail message using Sales Navigator

Open manuelrech opened this issue 2 years ago • 10 comments

Hello, I would like to send a message by using the premium tool Sales Navigator to non-connected people. Any idea how can I do this?

manuelrech avatar Jan 15 '23 10:01 manuelrech

@manuelrech were you able to accomplish this?

jatinvinkumar avatar Feb 10 '23 07:02 jatinvinkumar

Nope I decided to use Selenium to achieve that

manuelrech avatar Feb 12 '23 16:02 manuelrech

All requests should ofc contain standard headers. One way is to check for credits first here using GET: https://www.linkedin.com/sales-api/salesApiCredits?q=findCreditGrant&creditGrantType=LSS_INMAIL In response, you get something like this where value property (of first child of elements) is number of remaining credits { "elements": [ { "type": "LSS_INMAIL", "value": 150, "id": 76486807 } ], "paging": { "count": 10, "start": 0, "links": [] } }

Then actual send message request is here using POST: https://www.linkedin.com/sales-api/salesApiMessageActions?action=createMessage All standard headers should be included. Request body/payload: { "createMessageRequest": { "subject": "Hi", "body": "How are you?\n\n\nYOUR NAME", "trackingId": GENERATED, "copyToCrm": false, "recipients": [ "urn:li:fs_salesProfile:(ACwAADoB8t8Bn44ehqxo-qK8ZEWvGy6rQ3PhBC0,NAME_SEARCH,Kdir)" ] } }

M1z23R avatar Feb 18 '23 11:02 M1z23R

hi @M1z23R ! I'm trying what you suggested, but I'm getting the following error:

{'code': 'SALES_SEAT_REQUIRED', 'serviceErrorCode': 784523211, 'status': 403}

Any idea about what is going wrong based on that error? Here's my code:

from linkedin_api import Linkedin

api = Linkedin(email, password)

params = {
    'q': 'findCreditGrant',
    'creditGrantType': 'LSS_INMAIL',
}

r = api.client.session.get(url='https://www.linkedin.com/sales-api/salesApiCredits', params=params)

print(r.json())

Thanks!

WittmannF avatar Aug 30 '23 18:08 WittmannF

It's maybe a stupid question from me but, do you have Sales Navigator account or are you using free account? I can't remember - I think there are like 1,2,5 free InMails with free account so that's why I am asking

M1z23R avatar Aug 30 '23 18:08 M1z23R

Hi @M1z23R! I'm using a paid account, but maybe I should be authenticating directly to linkedin sales. If you have any code that you used at that time, I'd appreciate.

WittmannF avatar Aug 30 '23 18:08 WittmannF

I am not sure how familiar you are with JS, but I am writing a chrome extension, and I am looking up this repo for references, endpoints, payload structures, etc. Here is a code you can enter in console in dev tools in Chrome (while on linkedin):


    const getCSRF = () => {
        for (const cookie of document.cookie.split("; ")) {
            const key = cookie.split("=")[0];
            const value = cookie.split("=")[1];
            if (key == "JSESSIONID") {
                return value.substring(1, value.length - 1);
            }
        }
        return "";
    };

    const req = await fetch(
        `https://www.linkedin.com/sales-api/salesApiCredits?q=findCreditGrant&creditGrantType=LSS_INMAIL`, {
            method: "GET",
            mode: "cors",
            cache: "no-cache",
            credentials: "same-origin",
            headers: [
                ["csrf-token", getCSRF()],
                ["x-li-deco-include-micro-schema", "true"],
                ["x-restli-protocol-version", "2.0.0"],
            ],
            redirect: "follow",
        }
    );

    const data = await req.json();

    console.log(data);

})();

M1z23R avatar Aug 30 '23 19:08 M1z23R

@WittmannF Try to adjust it to your needs and try to make it work with this python tool, it should work the same way.

M1z23R avatar Aug 30 '23 19:08 M1z23R

Thanks @M1z23R ! I was able to convert your code to python using GPT:

import requests

def getCSRF(cookies):
    for cookie in cookies.split("; "):
        key = cookie.split("=")[0]
        value = cookie.split("=")[1]
        if key == "JSESSIONID":
            return value[1:-1]
    return ""

# Assuming you have cookies as a string (like in JS's document.cookie)
cookies_str = "YOUR_COOKIES_STRING_HERE"
csrf_token = getCSRF(cookies_str)

headers = {
    "csrf-token": csrf_token,
    "x-li-deco-include-micro-schema": "true",
    "x-restli-protocol-version": "2.0.0",
}

response = requests.get(
    "https://www.linkedin.com/sales-api/salesApiCredits?q=findCreditGrant&creditGrantType=LSS_INMAIL",
    headers=headers,
    cookies={"JSESSIONID": csrf_token},  # Assuming this is the only required cookie, otherwise add more as needed
)

data = response.json()

print(data)

What I feel is my current blocker is that this library is authenticating to https://www.linkedin.com/login and actually I wanted it to authenticate to https://www.linkedin.com/sales/login . Any insights on this are welcome. Thank you!

WittmannF avatar Aug 31 '23 17:08 WittmannF

From my experience with linkedin, it same domain - which means it's also same login process If you need any more help, let me know

M1z23R avatar Aug 31 '23 17:08 M1z23R