Python API Client for Close

Overview

Close API

PyPI version CircleCI

A convenient Python wrapper for the Close API.

Installation

pip install closeio

Sample Usage (of API client)

from closeio_api import Client

api = Client('YOUR_API_KEY')

# post a lead
lead = api.post('lead', data={'name': 'New Lead'})

# get 5 most recently updated opportunities
opportunities = api.get('opportunity', params={'_order_by': '-date_updated', '_limit': 5})

# fetch multiple leads (using search syntax)
lead_results = api.get('lead', params={
    '_limit': 10,
    '_fields': 'id,display_name,status_label',
    'query': 'custom.my_custom_field:"some_value" status:"Potential" sort:updated'
})

Example scripts

Check out https://github.com/closeio/closeio-api-scripts for helpful scripts already written to accomplish some common tasks.

Other Languages

There are unofficial API clients available in other languages too, thanks to some awesome contributors:

Comments
  • bulk_update_lead_info for adding a contact doesn't appear to work

    bulk_update_lead_info for adding a contact doesn't appear to work

    (closeio)--- Projects/closeio-api ‹master» python scripts/bulk_update_leads_info.py ~/Downloads/test.csv --api-key <redacted> --confirmed
    [2015-05-15 18:59:23,018] INFO Starting new HTTPS connection (1): app.close.io
    [2015-05-15 18:59:23,539] INFO line 2 updated: lead_iyTYlkzfVWKn0rH0x1yGrPX2vMOzzCsOdBK205WrzLk Plumbing Medic
    [2015-05-15 18:59:23,539] INFO summary: updated[1], new[0], skipped[0]
    (closeio)--- Projects/closeio-api ‹master» cat ~/Downloads/test.csv
    lead_id,contact_name,contact_phone,,,,,,
    lead_iyTYlkzfVWKn0rH0x1yGrPX2vMOzzCsOdBK205WrzLk,Conference Call Line,+1818-452-3980,,,,,,
    

    No contact added to https://app.close.io/lead/lead_iyTYlkzfVWKn0rH0x1yGrPX2vMOzzCsOdBK205WrzLk/

    bug 
    opened by anemitz 10
  • Clean up the code, add docstrings, and get rid of the poorly implemented async client

    Clean up the code, add docstrings, and get rid of the poorly implemented async client

    Right now the async client makes the code more complex and doesn't really add much value. I doubt it's used in the wild and if it is, then the usage is probably quite confusing and hacky. I think we might as well get rid of it.

    Examples of current confusing behavor:

    • api.get/post/put/delete is still synchronous
    • api.map expects a list of grequests.AsyncRequest, but there's no easy way to construct them.

    This PR introduces breaking changes and should be published along with a major version bump.

    TODO:

    • [x] Add mocked unit tests
    • [x] Bump version to v1.0
    • [x] Add a GitHub Release and publish on PyPI
    opened by wojcikstefan 8
  • Issue 59 - fix user reassign

    Issue 59 - fix user reassign

    Fixes #59

    All the changes are supported by TDD with functional tests hitting the live API.

    I'm not committing the tests but I can share them somewhere in case you want to have a look.

    /cc @thomasst

    opened by flevour 7
  • datetimes do not take timezone offset into account properly.

    datetimes do not take timezone offset into account properly.

    As an example, today I ran this request:

    resp = api.get('report/activity/ORGID', params=
    {'user_id':'USERID', 'date_start':'2018-01-02', 'date_end':'2018-01-02'})
    

    The response I got back was:

    {
        "revenue_created_annual_created": 0, 
        "opportunities_created": 0, 
        "revenue_lost_annual_created": 0, 
        "sms_sent": 0, 
        "leads_created": 0, 
        "opportunities_lost": 0, 
        "revenue_won_monthly": 0, 
        "revenue_won_annual": 0, 
        "leads_contacted": 3, 
        "revenue_won_one_time": 0, 
        "revenue_lost_annual": 0, 
        "revenue_lost_one_time_created": 0, 
        "revenue_lost_one_time": 0, 
        "revenue_created_monthly": 0, 
        "revenue_won_annual_created": 0, 
        "opportunities_won": 0, 
        "opportunities_created_created": 0, 
        "emails_sent": 0, 
        "revenue_created_monthly_created": 0, 
        "emails_received": 0, 
        "calls_duration_total": 0, 
        "sms_received": 0, 
        "calls_duration_average": 0, 
        "revenue_won_one_time_created": 0, 
        "revenue_created_one_time": 0, 
        "revenue_won_monthly_created": 0, 
        "revenue_lost_monthly_created": 0, 
        "opportunities_won_created": 0, 
        "opportunities_lost_created": 0, 
        "revenue_lost_monthly": 0, 
        "_queries": {
            "leads_contacted": "call(duration > 0  date >= 2018-01-01 date <= 2018-01-01) or email(direction:sent  date >= 2018-01-01 date <= 2018-01-01) or sms(direction:sent  date >= 2018-01-01 date <= 2018-01-01)", 
            "emails_received": "email(direction:received  date >= 2018-01-01 date <= 2018-01-01)", 
            "calls": "call( date >= 2018-01-01 date <= 2018-01-01)", 
            "opportunities_created": "opportunity( created >= 2018-01-01 created <= 2018-01-01)", 
            "opportunities_won_created": "opportunity(status_type:won  closed >= 2018-01-01 closed <= 2018-01-01)", 
            "opportunities_lost_created": "opportunity(status_type:lost  lost >= 2018-01-01 lost <= 2018-01-01)", 
            "sms_sent": "sms(direction:sent  date >= 2018-01-01 date <= 2018-01-01)", 
            "leads_created": " created >= 2018-01-01 created <= 2018-01-01", 
            "opportunities_won": "opportunity(status_type:won  closed >= 2018-01-01 closed <= 2018-01-01)", 
            "opportunities_created_created": "opportunity( created >= 2018-01-01 created <= 2018-01-01)", 
            "emails_sent": "email(direction:sent  date >= 2018-01-01 date <= 2018-01-01)", 
            "sms_received": "smsdate >= 2018-01-01 date <= 2018-01-01)", 
            "opportunities_lost": "opportunity(status_type:lost" lost >= 2018-01-01 lost <= 2018-01-01)"
        }, 
        "revenue_created_one_time_created": 0, 
        "calls": 0, 
        "revenue_created_annual": 0
    }
    
    

    since the bounding dates are 2018-01-01 and 2018-01-01, it means that we aren't correctly factoring in timezone when passing through date params.

    We need to add the tz.offset*-1 to the datetime in both directions for it to appear the same as it does in app.

    opened by eengoron 5
  • user_reassign script skips over objects

    user_reassign script skips over objects

    The user_reassign script doesn't update all the objects. It paginates through objects that match certain criteria (i.e. the old user), reassigns them to the new user, and then increases the offset to fetch the next batch. The problem is that when we reassign old objects, the results don't contain them anymore, and we end up skipping objects. We instead need to keep the offset at 0 and loop until no more objects are left (except for in the dry run).

    bug 
    opened by thomasst 5
  • Script to transfer leads (and nested contacts, notes, calls, etc.) from one organization to another

    Script to transfer leads (and nested contacts, notes, calls, etc.) from one organization to another

    Should take a search query as input and 2 API keys (from different orgs)

    Transfer all the leads matching the search query

    As well as all the nested objects on the leads (Contacts, Tasks, Opportunities), and the Calls & Note Activities

    Emails don't need to transfer since we'll sync them ourselves but all other lead data should transfer over.

    If lead statuses or opportunity statuses don't exist it should clearly print out that you need to set up those statuses within the destination organization first.

    enhancement 
    opened by anemitz 5
  • user reassign scripts doesn't appear to actually work

    user reassign scripts doesn't appear to actually work

    (closeio-api)--- lib/closeio-api ‹master» ./scripts/user_reassign.py --to-user-id user_wWDo6ETmCC9mSL3YQt0wdierIIKfZnR0LShSiVxEXBF --from-user-id user_fLuLxKSVFjJTePCe660YI2f2b5JpwoBjWQaPqWUjm5V -k <redacted> --all-tasks --all-opportunities -c
    

    Running the above command seems to modify the tasks and opportunities but if you run it again the see the same output / nothing actually changes.

    bug 
    opened by anemitz 4
  • Script to bulk update the country code of all addresses in leads matching a certain search query

    Script to bulk update the country code of all addresses in leads matching a certain search query

    bulk_update_address_countries.py

    inputs:

    • api key
    • old country code
    • new country code
    • leads search query (defaults to * sort:created if none) -- can be coded in script since it's hard to pass complicated queries (e.g. quotes) to cli args

    python cli script that takes these items as inputs. it should loop through each lead in the given search query, and if there are any addresses on that lead with the old country code then it should update the address with the new country code

    Would also like a read-only preview mode of what will happen and then a --confirmed flag

    @congocongo Can you work on this one after the other script?

    opened by philfreo 4
  • Add custom User Agent to every request, and expose the version in a readable way

    Add custom User Agent to every request, and expose the version in a readable way

    Closes #80

    This PR:

    • Changes the user agent sent on requests from python-requests/X to python closeio vY python-requests/X, where Y is the version of the closeio-api wrapper and X is the python-requests version
    • Adds a __version__ variable to __init__.py to keep track of the version.
    • Adds a helper to setup.py to read the __version__ variable from __init__.py to keep things consistent when installing the package.
    • Updates the version from 1.1 to 1.2

    I used the same structure as the Cleancat repo to do this.

    opened by eengoron 3
  • add pkg details to user agent

    add pkg details to user agent

    In response to https://github.com/closeio/closeio-api/issues/80.

    This modifies the default User-Agent to include package details: python closeio v{VERSION} {DEFAULT}

    Also breaks out the version constant into its' own version file. This helps to reduce the change of inconsistent versions, while also making it easy to add the seudo-standard __version__ attribute :)

    Testing

    Setup a simple server script to dump headers when making calls with the client. Then loaded the modified closeio_api and the output of the dump confirms this is working.

    ----- Request Start ----->
    
    /lead/
    Host: localhost:8000
    Connection: keep-alive
    Accept-Encoding: gzip, deflate
    Accept: */*
    User-Agent: python closeio v0.5 python-requests/2.11.1
    Content-Type: application/json
    X-TZ-Offset: -7
    Content-Length: 20
    Authorization: Basic abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx
    
    {"name": "New Lead"}
    <----- Request End -----
    
    127.0.0.1 - - [13/Mar/2017 17:25:57] "POST /lead/ HTTP/1.1" 200 -
    
    opened by JohnLZeller 3
  • Make the async version of the client easier to use

    Make the async version of the client easier to use

    This is an alternative to #78 fixing some of the async client's flaws instead of removing it altogether.

    Example usage:

    In [1]: from closeio_api import Client
    
    In [2]: api = Client('api_key', development=True, async=True)
    
    In [3]: results = api.map([
       ...:   api.post('lead', {'name': 'New Lead 1'}),
       ...:   api.post('lead', {'name': 'New Lead 2'}),
       ...:   api.post('lead', {'name': 'New Lead 3'}),
       ...:   api.post('lead', {'name': 'New Lead 4'})
       ...: ])
    
    In [4]: len(results)
    Out[4]: 4
    
    In [5]: results[0]['id']
    Out[5]: u'lead_RpzCIwspNlTUowYUvIUaNE1tp3ncVFk8x3Uw4sv96FO'
    

    I'm still conflicted whether such a deep integration of concurrency is good for this client library and its users or not. On the one hand, it makes concurrent requests easier to construct and send, and it handles API errors and retry logic properly (-ish). On the other hand, it makes the code more complex (and thus harder to read/understand), it forces a particular implementation of concurrency (green threads & gevent), and there are still some quirks with it (e.g. using the "debug" flag differs unintuitively between the sync and async client).

    @closeio/engineering what do you think?

    This PR introduces breaking changes and should be published along with a major version bump.

    opened by wojcikstefan 3
  • Update ratelimit behavior

    Update ratelimit behavior

    We want to update the behavior of our api and client to follow the draft RFC related to communicating rate limiting.

    See https://github.com/closeio/closeio/issues/28735

    opened by lmickh 0
  • APIError message should contain the response's status code

    APIError message should contain the response's status code

    So that it's obvious at a glance what kind of an error we experienced. Right now it's possible to get an enigmatic traceback:

    In [3]: api.get('me')
    Traceback (most recent call last)
    <ipython-input-3-842652bd220e> in <module>()
    ----> 1 api.get('me')
    
    /Users/wojcikstefan/Repos/closeio-api/closeio_api/__init__.py in get(self, endpoint, params, **kwargs)
        132         """
        133         kwargs.update({'params': params})
    --> 134         return self._dispatch('get', endpoint+'/', **kwargs)
        135
        136     def post(self, endpoint, data, **kwargs):
    
    /Users/wojcikstefan/Repos/closeio-api/closeio_api/__init__.py in _dispatch(self, method_name, endpoint, api_key, data, debug, **kwargs)
        108             raise ValidationError(response)
        109         else:
    --> 110             raise APIError(response)
        111
        112     def _get_rate_limit_sleep_time(self, response):
    
    APIError:
    
    opened by wojcikstefan 5
  • APIError is not very informative

    APIError is not very informative

    When a request fails because of an aborted connection or other unhandled exception the resulting APIError and the stack trace generated with it is not very informative. We should make the string representation of APIError include more detail.

    opened by lucasvo 1
Releases(v2.0)
  • v2.0(Apr 6, 2022)

    Changes in this release:

    • update 429 handling to use response headers rather than the json body
    • (Breaking Change) Drop python 2 support
    • Other minor or project-internal changes (update pytest, test on more recent python 3 versions, etc)
    Source code(tar.gz)
    Source code(zip)
  • v1.4(Dec 30, 2020)

  • 1.3(Jun 10, 2020)

  • v1.1(Feb 22, 2019)

  • v1.0(May 8, 2017)

    We're happy to publish a stable v1.0 of our Close.io API wrapper! The main highlights of this release are:

    • Significantly improved code quality, clarity, and documentation.
    • Automatic retrying of rate-limited requests.
    • Breaking change: Removal of the asynchronous code. If you relied on our async=True flag in the past, you'll have to refactor your application. You can choose to run multiple concurrent api.get/post/put/delete requests via threading, multiprocessing, concurrent.futures, gevent, etc., depending on your specific use case and environment.
    Source code(tar.gz)
    Source code(zip)
  • v0.5(Jan 24, 2017)

    • Fixed adding a trailing slash for GET requests.
    • Several fixes to the lead merge script.
    • Dropped flawed Python v3.x support.
    • Added script deleting tasks for inactive users.
    Source code(tar.gz)
    Source code(zip)
Owner
Close
The inside sales CRM of choice for SMBs. Join our eng team: http://jobs.close.com/
Close
PyFacebook

== PyFacebook == PyFacebook is a Python client library for the Facebook API. Samuel Cormier-Iijima ( Samuel Cormier-Iijima 573 Dec 20, 2022

Python Twitter API

Python Twitter Tools The Minimalist Twitter API for Python is a Python API for Twitter, everyone's favorite Web 2.0 Facebook-style status updater for

Mike Verdone 2.9k Jan 03, 2023
This is RequestTrackerBot and it used for tracking request made by user in a group

This is a Request Tracker Bot repo, It is for those who upload content like movies, anime, etc. It can be used for tracking request of content that your members asked for.

Abhijeet 27 Dec 29, 2022
KiKi bare dogs can share your joys and sorrows with you.

Kiki-FangLee-DiscordBot KiKi bare dogs can share your joys and sorrows with you. $help: Kiki will show you my talent, aw-aw. $list: Show Kiki's knowle

Fang Lee 0 Feb 12, 2022
doi, pubmed, arxiv.org的查询服务API接口,部署于vercel云函数

article-search-service doi, pubmed, arxiv.org的查询服务API接口,部署于vercel云函数 云函数 vercel,国内可能被qiang了。 DOI接口 POST https://article-search-service.vercel.app/api/

HyokaChen 2 Oct 10, 2021
Clisd.py - UI framework with client side rendering for python

clisd.py Clisd is UI framework with client side rendering for python. It uses WA

2 Mar 25, 2022
A discord bot with information and template tracking for pxls.space.

pyCharity A discord bot with information and template tracking for pxls.space. Inspired by Mikarific's Charity bot. Try out the beta version on your s

1 Dec 03, 2021
A cracking tool of Xiaomi Dr AI (Archytas / Archimedes)

Archytas Tool 我们强烈抵制闲鱼平台上未经授权的刷机服务! 我对本人之前在程序中为防止违规刷机服务添加未生效的格机代码感到抱歉,在此声明此过激行为与 Crack Mi Dr AI Team 无关,并将程序开源。 A cracking tool of Xiaomi Dr AI (Archy

rponeawa 5 Oct 25, 2022
Python library for the Stripe API.

Stripe Python Library The Stripe Python library provides convenient access to the Stripe API from applications written in the Python language. It incl

Stripe 1.3k Jan 03, 2023
Kevin L. 3 Jul 14, 2022
Creating a Python API, for the MakeMyTrip Flight Schedules.

MakeMyTripAPI Creating a Python API, for the MakeMyTrip Flight Schedules. Source: MakeMyTrip is an Indian online travel company founded in 2000. Headq

Aman Priyanshu 0 Jan 12, 2022
Singer Tap for dbt Artifacts built with the Meltano SDK

tap-dbt-artifacts tap-dbt-artifacts is a Singer tap for dbtArtifacts. Built with the Meltano SDK for Singer Taps.

Prratek Ramchandani 9 Nov 25, 2022
A Discord bot to scrape textfiles from messages and put them to Hastebin

A Discord bot to scrape textfiles from messages and put them to Hastebin. Intended to use on support servers to help users read textfiles on mobile.

1 Jan 23, 2022
discord vc exploit to lightly lag vcs

discord-vc-reconnector discord vc exploit to lag vcs how to use open the py file, then open devtools on discord, go to network and join a vc, dont sta

Tesco 30 Aug 09, 2022
Python3 based bittrex rest api wrapper

bittrex-rest-api This open source project was created to give an understanding of the Bittrex Rest API v1.1/v3.0 in pearl language. The sample file sh

4 Nov 15, 2022
A self-bot for discord, written in Python, which will send you notifications to your desktop if it detects an intruder on your discord server

A self-bot for discord, written in Python, which will send you notifications to your desktop if it detects an intruder on your discord server

LevPrav 1 Jan 11, 2022
This is a tool to help people to make a bot for labelling images for machine learning projects.

labeller_images_python_telegramBOT This is a bot to help collect data for any machine learning project. It was developed using the python-telegram-bot

Diego Silveira 2 Nov 13, 2021
Apple iTunes In-app purchase verification tool

itunes-iap v2 Python 2 & 3 compatible! Even with :mod:`asyncio` support! Source code: https://github.com/youknowone/itunes-iap Documentation: http://i

Jeong YunWon 129 Dec 10, 2022
Interact and easily use Google Chat room webhooks.

Chat Webhooks Easily interact and send messages with Google Chat's webhooks feature. This API is small, but should be a nice framework for working wit

BD103 2 Dec 13, 2021
S3-cleaner - A Python script attempts to delete the all objects/delete markers/versions from specific S3 bucket

Remove All Objects From S3 Bucket This Python script attempts to delete the all

9 Jan 27, 2022