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
A modern, easy to use, feature-rich, and async ready API wrapper improved and revived from original discord.py.

A Python API wrapper that is improved and revived from the original discord.py

Orion 19 Nov 06, 2021
Instagram GiftShop Scam Killer

Instagram GiftShop Scam Killer A basic tool for Windows which kills acess to any giftshop scam from Instagram. Protect your Instagram account from the

1 Mar 31, 2022
Monetize your apps with KivAds using Google AdMob api.

KivAds(WIP) Monetize your apps with KivAds using Google AdMob api. KivAds uses the latest version of Google AdMob sdk(version 20.0.0). KivAds exposes

Guhan Sensam 16 Nov 05, 2022
POC de uma AWS lambda que executa a consulta de preços de criptomoedas, e é implantada na AWS usando Github actions.

Cryptocurrency Prices Overview Instalação Repositório Configuração CI/CD Roadmap Testes Overview A ideia deste projeto é aplicar o conteúdo estudado s

Gustavo Santos 3 Aug 31, 2022
Heroku app to explore boardgame data

A Dashboard for the Board Game Geeks among us Link to Application As many Board Game Geeks like myself track the scores of board game matches I decide

Maarten Grootendorst 20 Nov 23, 2022
Simple Translator in Python

Simple Translator in Python Project Description: In this project, we'll be making a very simple translator in Python using some libraries. Requirement

Hassan Shahzad 3 Jan 23, 2022
This repository contains unofficial code reproducing Agent57

Agent57 This repository contains unofficial code reproducing Agent57, which outp

19 Dec 29, 2022
An API-driven solution for Makerspaces, Tinkerers, and Hackers.

Mventory is an API-driven inventory solution for Makers, Makerspaces, Hackspaces, and just about anyone else who needs to keep track of "stuff".

Matthew Macdonald-Wallace 107 Dec 21, 2022
A Python Client for News API

newsapi-python A Python client for the News API. License Provided under MIT License by Matt Lisivick. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRAN

Matt Lisivick 281 Dec 29, 2022
A Discord webhook spammer made in Python

A Python made Discord webhook spammer usually used for token loggers to spam them/delete them original by cattyn changes listed below.

2 Jan 12, 2022
RaidBot for WhatsApp

WhatsappRaid Скрипт подготовлен специально для сайта https://pysoc.ru и Ютуб канала PyPro Русский Простой спам бот для WhatsApp на Python3. Работает с

2 May 12, 2022
Data Platform com AWS CDK

Welcome to your CDK Python project! This is a blank project for Python development with CDK. The cdk.json file tells the CDK Toolkit how to execute yo

Andre Sionek 8 Jul 02, 2022
a simple quant trading bot with CLI interface

shepherd a simple quant trading bot with CLI interface CLI shell command docs coming soon after I brush up the code and add more features :) Minimal R

m00n 0 Jun 06, 2022
ShadowMusic - A Telegram Music Bot with proper functions written in Python with Pyrogram and Py-Tgcalls.

⭐️ Shadow Music ⭐️ A Telegram Music Bot written in Python using Pyrogram and Py-Tgcalls Ready to use method A Support Group, Updates Channel and ready

TeamShadow 8 Aug 17, 2022
PHION's client-side core python library

PHION-core PHION's client-side core python library. This library is not meant to be used directly by users. If you want to install phion please use th

PHION 2 Feb 07, 2022
Basic Discord python bot

#How to Create a Discord Bot Account In order to work with the Python library and the Discord API, we must first create a Discord Bot account. Here ar

Tustus 1 Oct 13, 2021
WhatsApp Multi Device Client

WhatsApp Multi Device Client

23 Nov 18, 2022
Discord bot to administer IITD Study Servers (unofficial)

IITD-Bot Discord bot to administer IITD'20 Acad Server Commands hello to check if bot is online ?help to display this message ?set kerberos to set y

Aditya Singh 47 Dec 19, 2022
Notion4ever - Python tool for export all your content of Notion page using official Notion API

NOTION4EVER Notion4ever is a small python tool that allows you to free your cont

50 Dec 30, 2022
Boilerplate template for the discord-py-interactions library

discord-py-interactions_boilerplate Boilerplate template for the discord-py-interactions library Currently, this boilerplate supports discord-py-inter

Ventus 7 Dec 03, 2022